ImageResizerCliExecutor.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Globalization; 7 using System.Linq; 8 using System.Threading; 9 10 using ImageResizer.Models; 11 using ImageResizer.Properties; 12 13 namespace ImageResizer.Cli 14 { 15 /// <summary> 16 /// Executes Image Resizer CLI operations. 17 /// Instance-based design for better testability and Single Responsibility Principle. 18 /// </summary> 19 public class ImageResizerCliExecutor 20 { 21 /// <summary> 22 /// Runs the CLI executor with the provided command-line arguments. 23 /// </summary> 24 /// <param name="args">Command-line arguments.</param> 25 /// <returns>Exit code.</returns> 26 public int Run(string[] args) 27 { 28 var cliOptions = CliOptions.Parse(args); 29 30 if (cliOptions.ParseErrors.Count > 0) 31 { 32 foreach (var error in cliOptions.ParseErrors) 33 { 34 Console.Error.WriteLine(error); 35 CliLogger.Error($"Parse error: {error}"); 36 } 37 38 CliOptions.PrintUsage(); 39 return 1; 40 } 41 42 if (cliOptions.ShowHelp) 43 { 44 CliOptions.PrintUsage(); 45 return 0; 46 } 47 48 if (cliOptions.ShowConfig) 49 { 50 CliOptions.PrintConfig(Settings.Default); 51 return 0; 52 } 53 54 if (cliOptions.Files.Count == 0 && string.IsNullOrEmpty(cliOptions.PipeName)) 55 { 56 Console.WriteLine(Resources.CLI_NoInputFiles); 57 CliOptions.PrintUsage(); 58 return 1; 59 } 60 61 return RunSilentMode(cliOptions); 62 } 63 64 private int RunSilentMode(CliOptions cliOptions) 65 { 66 var batch = ResizeBatch.FromCliOptions(Console.In, cliOptions); 67 var settings = Settings.Default; 68 CliSettingsApplier.Apply(cliOptions, settings); 69 70 CliLogger.Info($"CLI mode: processing {batch.Files.Count} files"); 71 72 // Use accessible line-based progress if requested or detected 73 bool useLineBasedProgress = cliOptions.ProgressLines ?? false; 74 int lastReportedMilestone = -1; 75 76 var errors = batch.Process( 77 (completed, total) => 78 { 79 var progress = (int)((completed / total) * 100); 80 81 if (useLineBasedProgress) 82 { 83 // Milestone-based progress (0%, 25%, 50%, 75%, 100%) 84 int milestone = (progress / 25) * 25; 85 if (milestone > lastReportedMilestone || completed == (int)total) 86 { 87 lastReportedMilestone = milestone; 88 Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Resources.CLI_ProgressFormat, progress, completed, (int)total)); 89 } 90 } 91 else 92 { 93 // Traditional carriage return mode 94 Console.Write(string.Format(CultureInfo.InvariantCulture, "\r{0}", string.Format(CultureInfo.InvariantCulture, Resources.CLI_ProgressFormat, progress, completed, (int)total))); 95 } 96 }, 97 settings, 98 CancellationToken.None); 99 100 if (!useLineBasedProgress) 101 { 102 Console.WriteLine(); 103 } 104 105 var errorList = errors.ToList(); 106 if (errorList.Count > 0) 107 { 108 Console.Error.WriteLine(string.Format(CultureInfo.InvariantCulture, Resources.CLI_CompletedWithErrors, errorList.Count)); 109 CliLogger.Error($"Processing completed with {errorList.Count} error(s)"); 110 foreach (var error in errorList) 111 { 112 Console.Error.WriteLine(string.Format(CultureInfo.InvariantCulture, " {0}: {1}", error.File, error.Error)); 113 CliLogger.Error($" {error.File}: {error.Error}"); 114 } 115 116 return 1; 117 } 118 119 CliLogger.Info("CLI batch completed successfully"); 120 Console.WriteLine(Resources.CLI_AllFilesProcessed); 121 return 0; 122 } 123 } 124 }