22{
33 using System ;
44 using System . Collections . Generic ;
5- using System . Linq ;
65 using System . Reflection ;
76 using CommandLine ;
87 using Microsoft . Extensions . Configuration ;
98 using Microsoft . Extensions . DependencyInjection ;
109 using Microsoft . Extensions . Hosting ;
11- using Microsoft . Extensions . Logging ;
1210 using Neolution . DotNet . Console . Abstractions ;
1311 using Neolution . DotNet . Console . Internal ;
14- using NLog . Extensions . Logging ;
1512
1613 /// <summary>
1714 /// The console application builder.
@@ -34,24 +31,9 @@ public class DotNetConsoleBuilder
3431 private readonly ServiceCollection serviceCollection = new ( ) ;
3532
3633 /// <summary>
37- /// List of configuration delegates to be applied during host building.
34+ /// The configuration manager
3835 /// </summary>
39- private readonly List < Action < HostBuilderContext , IConfigurationBuilder > > configurationDelegates = new ( ) ;
40-
41- /// <summary>
42- /// The initial configuration builder used to create dynamic configuration
43- /// </summary>
44- private readonly IConfigurationBuilder configurationBuilder ;
45-
46- /// <summary>
47- /// The host builder context used for dynamic configuration building
48- /// </summary>
49- private readonly HostBuilderContext hostBuilderContext ;
50-
51- /// <summary>
52- /// The built configuration root - built once when first accessed
53- /// </summary>
54- private IConfigurationRoot ? builtConfiguration ;
36+ private readonly ConsoleConfigurationManager configurationManager ;
5537
5638 /// <summary>
5739 /// Run only to check dependencies.
@@ -72,12 +54,14 @@ internal DotNetConsoleBuilder(IHostBuilder hostBuilder, ParserResult<object> com
7254 this . hostBuilder = hostBuilder ;
7355 this . commandLineParserResult = commandLineParserResult ;
7456 this . Environment = environment ;
75- this . configurationBuilder = configurationBuilder ;
76- this . hostBuilderContext = new HostBuilderContext ( new Dictionary < object , object > ( ) )
57+
58+ var hostBuilderContext = new HostBuilderContext ( new Dictionary < object , object > ( ) )
7759 {
7860 HostingEnvironment = environment ,
7961 Configuration = configurationBuilder . Build ( ) ,
8062 } ;
63+
64+ this . configurationManager = new ConsoleConfigurationManager ( configurationBuilder , hostBuilderContext ) ;
8165 }
8266
8367 /// <summary>
@@ -88,35 +72,7 @@ internal DotNetConsoleBuilder(IHostBuilder hostBuilder, ParserResult<object> com
8872 /// <summary>
8973 /// Gets a collection of configuration providers for the application to compose. This is useful for adding new configuration sources and providers.
9074 /// </summary>
91- public IConfiguration Configuration
92- {
93- get
94- {
95- // Build the configuration once when first accessed, similar to Microsoft's approach
96- if ( this . builtConfiguration == null )
97- {
98- // Create a new configuration builder based on the initial one
99- var configBuilder = new ConfigurationBuilder ( ) ;
100-
101- // Add all sources from the initial configuration builder
102- foreach ( var source in this . configurationBuilder . Sources )
103- {
104- configBuilder . Add ( source ) ;
105- }
106-
107- // Apply all configuration delegates that have been added via ConfigureAppConfiguration
108- foreach ( var configureDelegate in this . configurationDelegates )
109- {
110- configureDelegate ( this . hostBuilderContext , configBuilder ) ;
111- }
112-
113- // Build and store the configuration root
114- this . builtConfiguration = configBuilder . Build ( ) ;
115- }
116-
117- return this . builtConfiguration ;
118- }
119- }
75+ public IConfiguration Configuration => this . configurationManager . Configuration ;
12076
12177 /// <summary>
12278 /// Gets the collection of services for the application to compose. This is useful for adding user provided or framework provided services.
@@ -130,13 +86,7 @@ public IConfiguration Configuration
13086 /// <returns>The <see cref="DotNetConsoleBuilder"/>.</returns>
13187 public DotNetConsoleBuilder ConfigureAppConfiguration ( Action < HostBuilderContext , IConfigurationBuilder > configureDelegate )
13288 {
133- ArgumentNullException . ThrowIfNull ( configureDelegate ) ;
134-
135- this . configurationDelegates . Add ( configureDelegate ) ;
136-
137- // Reset the built configuration so it gets rebuilt on next access
138- this . builtConfiguration = null ;
139-
89+ this . configurationManager . AddConfigurationDelegate ( configureDelegate ) ;
14090 return this ;
14191 }
14292
@@ -186,103 +136,28 @@ internal static DotNetConsoleBuilder CreateBuilderInternal(Assembly assembly, Ty
186136 var configBuilder = DotNetConsoleDefaults . CreateConsoleConfigurationBuilder ( assembly , args , environment ) ;
187137
188138 // Create a HostBuilder
189- var builder = Host . CreateDefaultBuilder ( args )
190- . UseContentRoot ( environment . ContentRootPath )
191- . ConfigureLogging ( ( context , logging ) =>
192- {
193- AdjustDefaultBuilderLoggingProviders ( logging ) ;
194- logging . AddNLog ( context . Configuration ) ;
195- } )
196- . ConfigureServices ( ( _ , services ) =>
197- {
198- // Register all commands found in the entry assembly.
199- services . Scan ( selector => selector . FromAssemblies ( assembly )
200- . AddClasses ( classes => classes . AssignableTo ( typeof ( IDotNetConsoleCommand < > ) ) )
201- . AsImplementedInterfaces ( ) ) ;
202- } ) ;
139+ var builder = ConsoleHostBuilderConfigurator . CreateConfiguredHostBuilder ( assembly , args , environment ) ;
203140
204- // If verb types were not specified, compile all available verbs for this run by looking for classes with the Verb attribute in the specified assembly
205- verbTypes ??= assembly . GetTypes ( )
206- . Where ( t => t . GetCustomAttribute < VerbAttribute > ( ) != null )
207- . ToArray ( ) ;
208-
209- var parsedArguments = Parser . Default . ParseArguments ( args , verbTypes ) ;
141+ var parsedArguments = CommandLineProcessor . ParseArguments ( assembly , verbTypes , args ) ;
210142 var consoleBuilder = new DotNetConsoleBuilder ( builder , parsedArguments , environment , configBuilder ) ;
211143
212144 // Apply any custom configuration delegates that will be added later via ConfigureAppConfiguration
213145 builder . ConfigureAppConfiguration ( ( context , configBuilder ) =>
214146 {
215147 // Apply all stored configuration delegates
216- foreach ( var configureDelegate in consoleBuilder . configurationDelegates )
148+ foreach ( var configureDelegate in consoleBuilder . configurationManager . ConfigurationDelegates )
217149 {
218150 configureDelegate ( context , configBuilder ) ;
219151 }
220152 } ) ;
221153
222- if ( args . Length == 1 && string . Equals ( args [ 0 ] , "check-deps" , StringComparison . OrdinalIgnoreCase ) )
154+ if ( CommandLineProcessor . IsCheckDependenciesRequest ( args ) )
223155 {
224156 consoleBuilder . checkDependencies = true ;
225157 return consoleBuilder ;
226158 }
227159
228- CheckStrictVerbMatching ( args , verbTypes ) ;
229160 return consoleBuilder ;
230161 }
231-
232- /// <summary>
233- /// Adjusts the default builder logging providers.
234- /// </summary>
235- /// <param name="logging">The logging.</param>
236- private static void AdjustDefaultBuilderLoggingProviders ( ILoggingBuilder logging )
237- {
238- // Remove the default logging providers
239- logging . ClearProviders ( ) ;
240-
241- // Re-add other logging providers that are assigned in Host.CreateDefaultBuilder
242- logging . AddDebug ( ) ;
243- logging . AddEventSourceLogger ( ) ;
244-
245- if ( OperatingSystem . IsWindows ( ) )
246- {
247- // Add the EventLogLoggerProvider on windows machines
248- logging . AddEventLog ( ) ;
249- }
250- }
251-
252- /// <summary>
253- /// Enforce strict verb matching if one verb is marked as default. Otherwise, the default verb will be executed even if that was not the users intention.
254- /// </summary>
255- /// <param name="args">The arguments.</param>
256- /// <param name="availableVerbTypes">The available verb types.</param>
257- /// <exception cref="Neolution.DotNet.Console.DotNetConsoleException">Cannot create builder, because the specified verb '{firstVerb}' matches no command.</exception>
258- private static void CheckStrictVerbMatching ( string [ ] args , Type [ ] availableVerbTypes )
259- {
260- var availableVerbs = availableVerbTypes . Select ( t => t . GetCustomAttribute < VerbAttribute > ( ) ! ) . ToList ( ) ;
261- if ( ! availableVerbs . Any ( v => v . IsDefault ) )
262- {
263- // If no default verb is defined, we do not enforce strict verb matching
264- return ;
265- }
266-
267- var firstVerb = args . FirstOrDefault ( ) ;
268- if ( string . IsNullOrWhiteSpace ( firstVerb ) || firstVerb . StartsWith ( '-' ) )
269- {
270- // If the user passed no verb, but a default verb is defined, the default verb will be executed
271- return ;
272- }
273-
274- // Names reserved by CommandLineParser library
275- var validFirstArguments = new List < string > { "--help" , "--version" , "help" , "version" } ;
276-
277- // Names of all available verbs
278- validFirstArguments . AddRange ( availableVerbs . Select ( t => t . Name ) ) ;
279-
280- // Check if the first argument can be found in the list of valid arguments
281- var verbMatched = validFirstArguments . Any ( v => v . Equals ( firstVerb , StringComparison . OrdinalIgnoreCase ) ) ;
282- if ( ! verbMatched )
283- {
284- throw new DotNetConsoleException ( $ "Cannot create builder, because the specified verb '{ firstVerb } ' matches no command.") ;
285- }
286- }
287162 }
288163}
0 commit comments