-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
205 lines (159 loc) · 7.78 KB
/
Copy pathProgram.cs
File metadata and controls
205 lines (159 loc) · 7.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
using System.Diagnostics.CodeAnalysis;
using System.Text;
using FileCombiner.Modules;
using FileCombiner.Modules.CLI;
using FileCombiner.Modules.Configuration;
using FileCombiner.Modules.Services;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console;
namespace FileCombiner;
/// <summary>
/// Main program entry point with dependency injection and CLI orchestration.
/// </summary>
// ReSharper disable once ClassNeverInstantiated.Global
[ExcludeFromCodeCoverage] // Entry point - not unit testable
internal class Program
{
private static string Esc(string s)
{
return Markup.Escape(s);
}
/// <summary>
/// Wraps an async operation with timeout detection to diagnose hangs.
/// </summary>
private static async Task<T> WithTimeoutDetection<T>(Task<T> task, string operationName, int timeoutSeconds = 30)
{
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
var completedTask = await Task.WhenAny(task, timeoutTask);
if (completedTask == timeoutTask)
{
AnsiConsole.MarkupLine($"[red]WARNING:[/] Operation '{operationName}' has been running for {timeoutSeconds} seconds...");
AnsiConsole.MarkupLine("[yellow]This may indicate a deadlock or hang. Waiting for completion...[/]");
return await task; // Continue waiting but user is informed
}
return await task;
}
private static async Task<int> Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.UTF8;
// Parse command line options
var options = CommandLineInterface.Parse(args);
if (options == null) return 1;
// If interactive mode is requested, run interactive prompts with timeout
if (options.Interactive)
{
var interactiveTask = Task.Run(() => CommandLineInterface.RunInteractive());
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(60));
var completedTask = await Task.WhenAny(interactiveTask, timeoutTask);
if (completedTask == timeoutTask)
{
AnsiConsole.MarkupLine("[yellow]Interactive mode timed out after 60 seconds. Exiting...[/]");
return 0;
}
options = await interactiveTask;
}
try
{
// Validate directory
if (!Directory.Exists(options.Directory))
{
AnsiConsole.MarkupLine($"[red]Error:[/] Directory does not exist: {Esc(options.Directory)}");
return 1;
}
// Build app configuration from CLI
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Building app configuration from command line options...[/]");
var config = AppConfig.FromCommandLine(options);
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: App configuration created successfully[/]");
// Setup dependency injection
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Setting up dependency injection container...[/]");
var services = new ServiceCollection();
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Configuring services...[/]");
RunTimeUtils.ConfigureServices(services, options);
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Adding text parser services...[/]");
RunTimeUtils.AddTextParser(services, config);
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Building service provider...[/]");
else
AnsiConsole.MarkupLine("[grey]Building service provider...[/]");
await using var provider = services.BuildServiceProvider();
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Service provider built successfully[/]");
else
AnsiConsole.MarkupLine("[grey]Service provider built.[/]");
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Resolving IFileDiscoveryService...[/]");
var discoveryService = await Task.Run(() => provider.GetRequiredService<IFileDiscoveryService>());
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: IFileDiscoveryService resolved successfully[/]");
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Resolving IFileCombinerService...[/]");
var combinerService = await Task.Run(() => provider.GetRequiredService<IFileCombinerService>());
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: IFileCombinerService resolved successfully[/]");
else
AnsiConsole.MarkupLine("[grey]Services resolved successfully.[/]");
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Starting file discovery...[/]");
else
AnsiConsole.MarkupLine("[grey]Starting discovery...[/]");
// STEP 1: Discover files
AnsiConsole.MarkupLine("[bold]Discovering files...[/]");
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Calling DiscoverFilesAsync...[/]");
var discoveryResult = await WithTimeoutDetection(
discoveryService.DiscoverFilesAsync(config),
"File Discovery",
30
).ConfigureAwait(false);
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: DiscoverFilesAsync completed successfully[/]");
else
AnsiConsole.MarkupLine("[grey]Finished discovery...[/]");
if (discoveryResult.ProcessedFiles.Count == 0)
{
AnsiConsole.MarkupLine("[yellow]No files found to process.[/]");
return 0;
}
// STEP 2: Print summary
CommandLineInterface.PrintSummary(discoveryResult);
if (config.DryRun)
{
AnsiConsole.MarkupLine("[yellow]Dry run mode - no files will be processed[/]");
return 0;
}
// Confirm action
if (!await AnsiConsole.ConfirmAsync($"Proceed with combining these {discoveryResult.ProcessedFiles.Count} files?"))
{
AnsiConsole.MarkupLine("[yellow]Operation cancelled[/]");
return 0;
}
AnsiConsole.WriteLine();
// STEP 3: Combine files
AnsiConsole.MarkupLine("[bold]Combining files...[/]");
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: Calling CombineFilesAsync...[/]");
var combined = await WithTimeoutDetection(
combinerService.CombineFilesAsync(config, discoveryResult),
"File Combining",
60
).ConfigureAwait(false);
if (options.Verbose)
AnsiConsole.MarkupLine("[dim]DEBUG: CombineFilesAsync completed successfully[/]");
// STEP 4: Output result (delegated to CLI)
await CommandLineInterface.OutputResult(combined, config);
return 0;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error:[/] {Esc(ex.Message)}");
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths | ExceptionFormats.ShortenTypes);
return 1;
}
}
}