1
- using System . ComponentModel . Composition . Hosting ;
2
- using System . Reflection ;
3
- using Microsoft . DataTransfer . Interfaces ;
1
+ using System . CommandLine ;
4
2
using Microsoft . Extensions . Configuration ;
5
3
using Microsoft . Extensions . DependencyInjection ;
6
4
using Microsoft . Extensions . Hosting ;
7
- using Microsoft . Extensions . Logging ;
5
+ using System . CommandLine . Builder ;
6
+ using System . CommandLine . Hosting ;
7
+ using System . CommandLine . Parsing ;
8
+ using System . CommandLine . Help ;
8
9
9
10
namespace Microsoft . DataTransfer . Core ;
10
11
11
12
class Program
12
13
{
13
- public static async Task Main ( string [ ] args )
14
+ public static async Task < int > Main ( string [ ] args )
14
15
{
15
- using IHost host = Host . CreateDefaultBuilder ( args )
16
- . ConfigureAppConfiguration ( cfg =>
17
- {
18
- cfg . AddUserSecrets < Program > ( ) ;
19
- } )
20
- . Build ( ) ;
21
-
22
- IConfiguration configuration = host . Services . GetRequiredService < IConfiguration > ( ) ;
23
- var loggerFactory = host . Services . GetRequiredService < ILoggerFactory > ( ) ;
24
- var log = loggerFactory . CreateLogger < Program > ( ) ;
25
-
26
- var options = configuration . Get < DataTransferOptions > ( ) ;
27
-
28
- var hostingProcess = host . RunAsync ( ) ;
29
-
30
- var catalog = new AggregateCatalog ( ) ;
31
- string extensionsPath = GetExtensionFolderPath ( configuration , log ) ;
32
- log . LogInformation ( "Loading extensions from {ExtensionsPath}" , extensionsPath ) ;
33
- catalog . Catalogs . Add ( new DirectoryCatalog ( extensionsPath , "*Extension.dll" ) ) ;
34
- var container = new CompositionContainer ( catalog ) ;
35
-
36
- var sources = LoadExtensions < IDataSourceExtension > ( container ) ;
37
- var sinks = LoadExtensions < IDataSinkExtension > ( container ) ;
38
-
39
- log . LogInformation ( "{TotalExtensionCount} Extensions Loaded" , sources . Count + sinks . Count ) ;
40
-
41
- var source = GetExtensionSelection ( options . Source , sources , "Source" ) ;
42
- var sourceConfig = BuildSettingsConfiguration ( configuration , options . SourceSettingsPath , $ "{ source . DisplayName } SourceSettings", options . Source == null ) ;
43
-
44
- var sink = GetExtensionSelection ( options . Sink , sinks , "Sink" ) ;
45
- var sinkConfig = BuildSettingsConfiguration ( configuration , options . SinkSettingsPath , $ "{ sink . DisplayName } SinkSettings", options . Sink == null ) ;
46
-
47
- var data = source . ReadAsync ( sourceConfig , loggerFactory . CreateLogger ( source . GetType ( ) . Name ) ) ;
48
- await sink . WriteAsync ( data , sinkConfig , source , loggerFactory . CreateLogger ( sink . GetType ( ) . Name ) ) ;
49
-
50
- log . LogInformation ( "Done" ) ;
16
+ var rootCommand = new RootCommand ( "Azure data migration tool" ) { TreatUnmatchedTokensAsErrors = false } ;
17
+ rootCommand . AddCommand ( new RunCommand ( ) ) ;
18
+ rootCommand . AddCommand ( new ListCommand ( ) ) ;
51
19
52
- Console . WriteLine ( "Enter to Quit..." ) ;
53
- Console . ReadLine ( ) ;
20
+ var cmdlineBuilder = new CommandLineBuilder ( rootCommand ) ;
54
21
55
- await host . StopAsync ( ) ;
56
- await hostingProcess ;
57
- }
58
-
59
- private static string GetExtensionFolderPath ( IConfiguration configuration , ILogger logger )
60
- {
61
- var configPath = configuration . GetValue < string > ( "ExtensionsPath" ) ;
62
- if ( ! string . IsNullOrWhiteSpace ( configPath ) )
63
- {
64
- try
22
+ var parser = cmdlineBuilder . UseHost ( _ => Host . CreateDefaultBuilder ( args ) ,
23
+ builder =>
65
24
{
66
- var fullPath = Path . GetFullPath ( configPath ) ;
67
- if ( ! Directory . Exists ( fullPath ) )
25
+ builder . ConfigureAppConfiguration ( cfg =>
68
26
{
69
- Directory . CreateDirectory ( fullPath ) ;
70
- }
71
-
72
- return fullPath ;
73
- }
74
- catch ( Exception ex )
75
- {
76
- logger . LogWarning ( ex , "Configured path {ExtensionsPath} is invalid. Using default instead." , configPath ) ;
77
- }
78
- }
79
-
80
- var exeFolder = AppContext . BaseDirectory ;
81
- var path = Path . Combine ( exeFolder , "Extensions" ) ;
82
- var di = new DirectoryInfo ( path ) ;
83
- if ( ! di . Exists )
84
- {
85
- di . Create ( ) ;
86
- }
87
- return di . FullName ;
88
- }
89
-
90
- private static List < T > LoadExtensions < T > ( CompositionContainer container )
91
- where T : class , IDataTransferExtension
92
- {
93
- var sources = new List < T > ( ) ;
94
-
95
- foreach ( var exportedExtension in container . GetExports < T > ( ) )
96
- {
97
- sources . Add ( exportedExtension . Value ) ;
98
- }
99
-
100
- return sources ;
101
- }
102
-
103
- private static T GetExtensionSelection < T > ( string ? selectionName , List < T > extensions , string inputPrompt )
104
- where T : class , IDataTransferExtension
105
- {
106
- if ( ! string . IsNullOrWhiteSpace ( selectionName ) )
107
- {
108
- var extension = extensions . FirstOrDefault ( s => selectionName . Equals ( s . DisplayName , StringComparison . OrdinalIgnoreCase ) ) ;
109
- if ( extension != null )
110
- {
111
- Console . WriteLine ( $ "Using { extension . DisplayName } { inputPrompt } ") ;
112
- return extension ;
113
- }
114
- }
115
-
116
- Console . WriteLine ( $ "Select { inputPrompt } ") ;
117
- for ( var index = 0 ; index < extensions . Count ; index ++ )
118
- {
119
- var extension = extensions [ index ] ;
120
- Console . WriteLine ( $ "{ index + 1 } :{ extension . DisplayName } ") ;
121
- }
122
-
123
- string ? selection = "" ;
124
- int input ;
125
- while ( ! int . TryParse ( selection , out input ) || input > extensions . Count )
126
- {
127
- selection = Console . ReadLine ( ) ;
128
- }
27
+ cfg . AddUserSecrets < Program > ( ) ;
28
+ } ) . ConfigureServices ( ( hostContext , services ) =>
29
+ {
30
+ services . AddTransient < ExtensionLoader > ( ) ;
31
+ } )
32
+ . UseCommandHandler < RunCommand , RunCommand . CommandHandler > ( )
33
+ . UseCommandHandler < ListCommand , ListCommand . CommandHandler > ( ) ;
34
+ } )
35
+ . UseHelp ( AddAdditionalArgumentsHelp )
36
+ . UseDefaults ( ) . Build ( ) ;
129
37
130
- return extensions [ input - 1 ] ;
38
+ return await parser . InvokeAsync ( args ) ;
131
39
}
132
40
133
- private static IConfiguration BuildSettingsConfiguration ( IConfiguration configuration , string ? settingsPath , string configSection , bool promptForFile )
41
+ private static void AddAdditionalArgumentsHelp ( HelpContext helpContext )
134
42
{
135
- IConfigurationBuilder configurationBuilder = new ConfigurationBuilder ( ) ;
136
- if ( ! string . IsNullOrEmpty ( settingsPath ) )
137
- {
138
- configurationBuilder = configurationBuilder . AddJsonFile ( settingsPath ) ;
139
- }
140
- else if ( promptForFile )
43
+ helpContext . HelpBuilder . CustomizeLayout ( _ =>
141
44
{
142
- Console . Write ( $ "Load settings from a file? (y/n):") ;
143
- var response = Console . ReadLine ( ) ;
144
- if ( IsYesResponse ( response ) )
145
- {
146
- Console . Write ( "Path to file: " ) ;
147
- var path = Console . ReadLine ( ) ;
148
- if ( ! string . IsNullOrWhiteSpace ( path ) )
149
- {
150
- configurationBuilder = configurationBuilder . AddJsonFile ( path ) ;
151
- }
152
- }
153
- else
45
+ var layout = HelpBuilder . Default . GetLayout ( ) . ToList ( ) ;
46
+ layout . Remove ( HelpBuilder . Default . AdditionalArgumentsSection ( ) ) ;
47
+ if ( helpContext . Command . GetType ( ) == typeof ( RunCommand ) )
154
48
{
155
- Console . Write ( $ "Configuration section to read settings? (default={ configSection } ):") ;
156
- response = Console . ReadLine ( ) ;
157
- if ( ! string . IsNullOrWhiteSpace ( response ) )
49
+ layout . Add ( ctx =>
158
50
{
159
- configSection = response ;
160
- }
51
+ ctx . Output . WriteLine ( "Additional Arguments:" ) ;
52
+ ctx . Output . WriteLine ( " Extension specific settings can be provided as additional arguments in the form:" ) ;
53
+ ctx . HelpBuilder . WriteColumns ( new List < TwoColumnHelpRow > { new ( "--<extension><Source|Sink>Settings:<name> <value>" , "ex: --JsonSourceSettings:FilePath MyDataFile.json" ) } . AsReadOnly ( ) , ctx ) ;
54
+ } ) ;
161
55
}
162
- }
163
-
164
- return configurationBuilder
165
- . AddConfiguration ( configuration . GetSection ( configSection ) )
166
- . Build ( ) ;
167
- }
168
-
169
- private static bool IsYesResponse ( string ? response )
170
- {
171
- if ( response ? . Equals ( "y" , StringComparison . CurrentCultureIgnoreCase ) == true )
172
- return true ;
173
- if ( response ? . Equals ( "yes" , StringComparison . CurrentCultureIgnoreCase ) == true )
174
- return true ;
175
56
176
- return false ;
57
+ return layout ;
58
+ } ) ;
177
59
}
178
- }
60
+ }
0 commit comments