Skip to content

Commit cf3870c

Browse files
author
DerEffi
committed
Added custom option validation
1 parent 0c443d1 commit cf3870c

File tree

4 files changed

+92
-43
lines changed

4 files changed

+92
-43
lines changed

ImageComparison/Models/Options.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class Options
3737

3838
[Option('m', "mode", Required = false, HelpText = @"Search mode, which images to compare to another
3939
40-
All : compare all images (default
40+
All : compare all images (default)
4141
Inclusive : compare only within directories
4242
Exclusive : compare only in different directories
4343
ListInclusive : compare only within the same

ImageComparison/Program.cs

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using CommandLine.Text;
33
using ImageComparison.Models;
44
using ImageComparison.Services;
5-
using Microsoft.CodeAnalysis.CSharp.Syntax;
6-
using System.IO;
75
using System.Reflection;
86

97
namespace ImageComparison
@@ -15,6 +13,22 @@ internal class Program
1513
public static bool force = false;
1614
public static ProgressBar? progress = null;
1715

16+
private static readonly List<string> preOptions =
17+
new List<string>(){
18+
"\n\nUSAGE:",
19+
$"\n {Assembly.GetExecutingAssembly().GetName().Name}.exe [options...]",
20+
"\n\nOPTIONS:",
21+
};
22+
private static readonly List<string> postOptions = new List<string>()
23+
{
24+
"EXIT CODES:",
25+
"",
26+
" 0 - Success",
27+
" 1 - General Error",
28+
" 11 - Warning",
29+
" 127 - Bad Request"
30+
};
31+
1832
public static int Main(string[] args)
1933
{
2034
ParserResult<Options> parser = new Parser(with => {
@@ -29,6 +43,53 @@ public static int Main(string[] args)
2943
LogService.OnLog += OnLog;
3044
CompareService.OnProgress += OnProgress;
3145

46+
// custom validation
47+
List<string> customErrors = new();
48+
49+
if (options.Processors.Count() == 0 && (options.Action == Models.Action.Move || options.Action == Models.Action.Bin || options.Action == Models.Action.Delete))
50+
customErrors.Add($"Required option 'p, processors' missing on action '{options.Action}'");
51+
52+
if (options.Similarity < 0 || options.Similarity > 10000)
53+
customErrors.Add("Option 's, similarity' is out of range. Valid 0 - 10000");
54+
55+
if (options.HashDetail <= 0 || options.HashDetail >= 10)
56+
customErrors.Add("Option 'd, detail' is out of range. Valid 1 - 99");
57+
58+
if (options.Target.Length == 0 && options.Action == Models.Action.Move)
59+
customErrors.Add($"Required option 't, target' missing on action '{options.Action}'");
60+
61+
if (options.Cache.Length == 0 && options.Action == Models.Action.NoMatch)
62+
customErrors.Add($"Required option 'c, cache' missing on action '{options.Action}'");
63+
64+
if (customErrors.Count > 0)
65+
{
66+
ParserResult<Options> errorParser = new Parser(with =>
67+
{
68+
with.HelpWriter = null;
69+
}).ParseArguments<Options>(new string[] { "--help" });
70+
71+
List<string> customErrorsText = new()
72+
{
73+
"\n\nERROR(S):"
74+
};
75+
customErrorsText.AddRange(customErrors.Select(e => " " + e));
76+
customErrorsText.AddRange(preOptions);
77+
78+
errorParser.WithNotParsed(error =>
79+
{
80+
Console.WriteLine(
81+
HelpText.AutoBuild(errorParser, h =>
82+
{
83+
h.AddEnumValuesToHelpText = false;
84+
return h;
85+
})
86+
.AddPreOptionsLines(customErrorsText)
87+
.AddPostOptionsLines(postOptions)
88+
);
89+
});
90+
return;
91+
}
92+
3293
try
3394
{
3495
string hashVersion = HashService.GetIdentifier(options.HashDetail, options.HashAlgorithm);
@@ -37,9 +98,12 @@ public static int Main(string[] args)
3798
options.Locations = options.Locations.Select(l => Path.IsPathRooted(l) ? l : Path.Combine(Assembly.GetExecutingAssembly().Location, l));
3899
List<List<FileInfo>> searchLocations = FileService.SearchProcessableFiles(options.Locations.ToArray(), options.Recursive);
39100

40-
FileService.DataDirectory = Path.IsPathRooted(options.Cache) ? options.Cache : Path.Combine(Assembly.GetExecutingAssembly().Location, Path.GetDirectoryName(options.Cache) ?? ".\\");
101+
FileService.DataDirectory = Path.IsPathRooted(options.Cache) ? (Path.GetDirectoryName(options.Cache) ?? FileService.DataDirectory) : Path.Combine(Assembly.GetExecutingAssembly().Location, Path.GetDirectoryName(options.Cache) ?? ".\\");
41102
FileService.CacheFile = Path.GetFileName(options.Cache).NullIfEmpty() ?? FileService.CacheFile;
103+
if (options.Cache.Length >= 1)
104+
CacheService.Init();
42105
List<CacheItem> cachedAnalysis = options.Cache.Length >= 1 ? CacheService.GetImages(hashVersion) : new();
106+
43107
List<List<ImageAnalysis>> analysedImages = CompareService.AnalyseImages(searchLocations, options.HashDetail, options.HashAlgorithm, cachedAnalysis);
44108
if (options.Cache.Length >= 1)
45109
CacheService.UpdateImages(analysedImages.SelectMany(i => i).ToList(), hashVersion, scantime);
@@ -73,6 +137,8 @@ public static int Main(string[] args)
73137
{
74138
case Models.Action.Move:
75139
deleteAction = DeleteAction.Move;
140+
if (!options.Target.EndsWith("\\") && !options.Target.EndsWith("/"))
141+
options.Target += "\\";
76142
break;
77143
case Models.Action.Delete:
78144
deleteAction = DeleteAction.Delete;
@@ -107,7 +173,7 @@ public static int Main(string[] args)
107173
try
108174
{
109175
processedFiles.Add(image.Image.FullName);
110-
FileService.DeleteFile(m.Image1.Image.FullName, deleteAction, options.Target, Path.IsPathRooted(options.Target));
176+
FileService.DeleteFile(image.Image.FullName, deleteAction, options.Target, !Path.IsPathRooted(options.Target));
111177
}
112178
catch { }
113179
}
@@ -130,7 +196,15 @@ public static int Main(string[] args)
130196
})
131197
.WithNotParsed(errors =>
132198
{
133-
DisplayHelp(parser);
199+
Console.WriteLine(
200+
HelpText.AutoBuild(parser, h =>
201+
{
202+
h.AddEnumValuesToHelpText = false;
203+
return h;
204+
})
205+
.AddPreOptionsLines(preOptions)
206+
.AddPostOptionsLines(postOptions)
207+
);
134208

135209
if (!errors.Any(e => e.GetType() == typeof(HelpRequestedError)))
136210
{
@@ -156,6 +230,9 @@ public static void OnProgress(object? sender, ImageComparerEventArgs e)
156230
}
157231
}
158232

233+
/*
234+
* Log implementation for console output
235+
*/
159236
private static void OnLog(object? sender, LogEventArgs logEvent)
160237
{
161238
if (logLevel <= logEvent.Log.LogLevel)
@@ -182,35 +259,5 @@ private static string ToLiteral(string valueTextForCompiler)
182259
{
183260
return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(valueTextForCompiler, false);
184261
}
185-
186-
private static void DisplayHelp<T>(ParserResult<T> result)
187-
{
188-
Console.WriteLine(
189-
HelpText.AutoBuild(result, h =>
190-
{
191-
h.AddEnumValuesToHelpText = false;
192-
return h;
193-
})
194-
.AddPreOptionsLines(
195-
new List<string>()
196-
{
197-
"\n\nUSAGE:",
198-
$"\n {Assembly.GetExecutingAssembly().GetName().Name}.exe [options...]",
199-
"\n\nOPTIONS:"
200-
}
201-
)
202-
.AddPostOptionsLines(
203-
new List<string>()
204-
{
205-
"EXIT CODES:",
206-
"",
207-
" 0 - Success",
208-
" 1 - General Error",
209-
" 11 - Warning",
210-
" 127 - Bad Request"
211-
}
212-
)
213-
);
214-
}
215262
}
216263
}

ImageComparison/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"ImageComparison": {
44
"commandName": "Project",
5-
"commandLineArgs": "--help -a Search -l \"D:\\Bilder\\Eigene Aufnahmen\\2023\" \"D:\\Bilder\\Eigene Aufnahmen\\2024\" -r -o Info -f -p Test Test1 Test2"
5+
"commandLineArgs": "--help"
66
}
77
}
88
}

ImageComparison/Services/CompareService.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class ImageComparerEventArgs
1414

1515
public static class CompareService
1616
{
17-
public readonly static string[] SupportedFileTypes = { ".bmp", ".dib", ".jpg", ".jpeg", ".jpe", ".png", ".pbm", ".pgm", ".ppm", ".sr", ".ras", ".tiff", ".tif", ".exr", ".jp2" };
17+
public readonly static string[] SupportedFileTypes = { ".bmp", ".dib", ".jpg", ".jpeg", ".jpe", ".png", ".pbm", ".pgm", ".ppm", ".sr", ".ras", ".tiff", ".tif", ".exr", ".jp2", ".ico" };
1818
#if DEBUG
1919
//only use single thread for breakpoints
2020
public static readonly int threadCount = 8;
@@ -99,9 +99,10 @@ public static class CompareService
9999
return analysed.Select(a => a.ToList()).ToList();
100100
}
101101

102-
public static List<ImageMatch> SearchForDuplicates(List<List<ImageAnalysis>> analysedLocations, int matchThreashold, SearchMode mode, List<NoMatch>? nomatches, CancellationToken token = new())
102+
public static List<ImageMatch> SearchForDuplicates(List<List<ImageAnalysis>> analysedLocations, int matchThreashold, SearchMode mode, List<NoMatch>? nomatches, CancellationToken token = new(), bool subsearch = false)
103103
{
104-
LogService.Log($"Comparing images from {analysedLocations.Count} locations in mode '{mode}'");
104+
if(!subsearch)
105+
LogService.Log($"Comparing images from {analysedLocations.Count} locations in mode '{mode}'");
105106

106107
nomatches ??= new();
107108

@@ -150,7 +151,7 @@ public static class CompareService
150151
});
151152
});
152153

153-
return SortMatches(comparisons);
154+
return subsearch ? comparisons.ToList() : SortMatches(comparisons);
154155
case SearchMode.ListInclusive:
155156
return SortMatches(analysedLocations
156157
.SelectMany(location => SearchForDuplicates(location, matchThreashold, nomatches, token))
@@ -168,7 +169,8 @@ public static class CompareService
168169
matchThreashold,
169170
SearchMode.ListExclusive,
170171
nomatches,
171-
token));
172+
token,
173+
true));
172174
case SearchMode.Inclusive:
173175
return SortMatches(analysedLocations
174176
.SelectMany(images => {
@@ -182,7 +184,7 @@ public static class CompareService
182184
}
183185
}
184186

185-
public static List<ImageMatch> SearchForDuplicates(List<ImageAnalysis> images, int matchThreashold, List<NoMatch> nomatches, CancellationToken token = new())
187+
private static List<ImageMatch> SearchForDuplicates(List<ImageAnalysis> images, int matchThreashold, List<NoMatch> nomatches, CancellationToken token = new())
186188
{
187189
nomatches ??= new();
188190

0 commit comments

Comments
 (0)