44using System . CommandLine ;
55using System . CommandLine . Binding ;
66using System . Linq ;
7+ using System . Threading . Tasks ;
78
89namespace Cognite . Extractor . Utils . CommandLine
910{
1011 /// <summary>
1112 /// Binder class for objects built with CommandLineOptionAttribute.
1213 /// </summary>
1314 /// <typeparam name="T">Type to create. Must have a parameterless constructor.</typeparam>
14- public class AttributeBinder < T > : BinderBase < T >
15+ public class AttributeBinder < T >
1516 {
1617 /// <summary>
1718 /// Built options, options here can be modified.
@@ -35,11 +36,11 @@ public AttributeBinder()
3536 /// <param name="command">Command to add to</param>
3637 public void AddOptionsToCommand ( Command command )
3738 {
38- if ( command == null ) throw new ArgumentNullException ( nameof ( Command ) ) ;
39+ if ( command == null ) throw new ArgumentNullException ( nameof ( command ) ) ;
3940
4041 foreach ( var kvp in Options )
4142 {
42- command . AddOption ( kvp . Value ) ;
43+ command . Add ( kvp . Value ) ;
4344 }
4445 }
4546
@@ -64,7 +65,8 @@ private void BuildCommandOptions()
6465 }
6566 Option option ;
6667 var optType = typeof ( Option < > ) . MakeGenericType ( prop . PropertyType ) ;
67- option = ( Option ) Activator . CreateInstance ( optType , aliases . ToArray ( ) , attr . Description ) ! ;
68+ option = ( Option ) Activator . CreateInstance ( optType , aliases . ToArray ( ) ) ! ;
69+ option . Description = attr . Description ;
6870
6971 Options [ prop . Name ] = option ;
7072 }
@@ -74,26 +76,111 @@ private void BuildCommandOptions()
7476 /// Create an instance of <typeparamref name="T"/>, and bind to it using options constructed from
7577 /// CommandLineOptionAttributes.
7678 /// </summary>
77- /// <param name="bindingContext">Context </param>
79+ /// <param name="result">Parse result </param>
7880 /// <returns>An instance of <typeparamref name="T"/> with fields filled from command line.</returns>
79- protected override T GetBoundValue ( BindingContext bindingContext )
81+ public T GetBoundValue ( ParseResult result )
8082 {
81- if ( bindingContext == null ) throw new ArgumentNullException ( nameof ( bindingContext ) ) ;
83+ if ( result == null ) throw new ArgumentNullException ( nameof ( result ) ) ;
8284
8385 var type = typeof ( T ) ;
84- var result = Activator . CreateInstance ( type ) ;
86+ var instance = Activator . CreateInstance ( type ) ;
8587 var props = type . GetProperties ( System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . Public ) ;
8688
8789 foreach ( var prop in props )
8890 {
8991 if ( ! prop . CanWrite ) continue ;
9092 if ( ! Options . TryGetValue ( prop . Name , out var option ) ) continue ;
9193
92- var res = bindingContext . ParseResult . GetValueForOption ( option ) ;
93- prop . SetValue ( result , res ) ;
94+ var getter = typeof ( ParseResult ) . GetMethods ( ) . First ( m =>
95+ m . Name == nameof ( ParseResult . GetValue )
96+ && m . IsGenericMethod
97+ && m . GetParameters ( ) [ 0 ] . ParameterType . IsGenericType
98+ && m . GetParameters ( ) [ 0 ] . ParameterType . GetGenericTypeDefinition ( ) == typeof ( Option < > )
99+ ) . MakeGenericMethod ( prop . PropertyType ) ;
100+ prop . SetValue ( instance , getter . Invoke ( result , new object [ ] { option } ) ) ;
94101 }
95102
96- return ( T ) result ! ;
103+ return ( T ) instance ! ;
97104 }
98105 }
106+
107+ /// <summary>
108+ /// Extensions for Command, to make our command line interface backwards compatible.
109+ /// </summary>
110+ public static class CommandExtensions
111+ {
112+ /// <summary>
113+ /// Set a typed handler for the command.
114+ /// </summary>
115+ /// <typeparam name="T">The type of the bound value.</typeparam>
116+ /// <param name="command">Command to set the handler for.</param>
117+ /// <param name="action">The action to execute when the command is invoked.</param>
118+ /// <param name="binder">The attribute binder to use for binding command line options.</param>
119+ /// <exception cref="ArgumentNullException"></exception>
120+ public static void SetHandler < T > ( this Command command , Action < T > action , AttributeBinder < T > binder )
121+ {
122+ if ( command == null ) throw new ArgumentNullException ( nameof ( command ) ) ;
123+ if ( action == null ) throw new ArgumentNullException ( nameof ( action ) ) ;
124+ if ( binder == null ) throw new ArgumentNullException ( nameof ( binder ) ) ;
125+
126+ command . SetAction ( result =>
127+ {
128+ var boundValue = binder . GetBoundValue ( result ) ;
129+ action ( boundValue ) ;
130+ } ) ;
131+ }
132+
133+ /// <summary>
134+ /// Set a typed async handler for the command.
135+ /// </summary>
136+ /// <typeparam name="T">The type of the bound value.</typeparam>
137+ /// <param name="command">Command to set the handler for.</param>
138+ /// <param name="action">The action to execute when the command is invoked.</param>
139+ /// <param name="binder">The attribute binder to use for binding command line options.</param>
140+ /// <exception cref="ArgumentNullException"></exception>
141+ public static void SetHandler < T > ( this Command command , Func < T , Task > action , AttributeBinder < T > binder )
142+ {
143+ if ( command == null ) throw new ArgumentNullException ( nameof ( command ) ) ;
144+ if ( action == null ) throw new ArgumentNullException ( nameof ( action ) ) ;
145+ if ( binder == null ) throw new ArgumentNullException ( nameof ( binder ) ) ;
146+
147+ command . SetAction ( async result =>
148+ {
149+ var boundValue = binder . GetBoundValue ( result ) ;
150+ await action ( boundValue ) . ConfigureAwait ( false ) ;
151+ } ) ;
152+ }
153+
154+
155+ /// <summary>
156+ /// Invoke the command.
157+ /// </summary>
158+ /// <param name="command">Command to invoke.</param>
159+ /// <param name="args">Raw command line arguments</param>
160+ /// <returns>The command's exit code.</returns>
161+ /// <exception cref="ArgumentNullException"></exception>
162+ public static int Invoke ( this Command command , string [ ] args )
163+ {
164+ if ( command == null ) throw new ArgumentNullException ( nameof ( command ) ) ;
165+ if ( args == null ) throw new ArgumentNullException ( nameof ( args ) ) ;
166+
167+ return command . Parse ( args ) . Invoke ( ) ;
168+ }
169+
170+ /// <summary>
171+ /// Invoke the command asynchronously.
172+ /// </summary>
173+ /// <param name="command">Command to invoke.</param>
174+ /// <param name="args">Raw command line arguments</param>
175+ /// <returns>A task representing the asynchronous operation, with the command's exit code as the result.</returns>
176+ /// <exception cref="ArgumentNullException"></exception>
177+ public static async Task < int > InvokeAsync ( this Command command , string [ ] args )
178+ {
179+ if ( command == null ) throw new ArgumentNullException ( nameof ( command ) ) ;
180+ if ( args == null ) throw new ArgumentNullException ( nameof ( args ) ) ;
181+
182+ return await command . Parse ( args ) . InvokeAsync ( ) . ConfigureAwait ( false ) ;
183+ }
184+
185+ }
99186}
0 commit comments