1+ using System ;
2+ using System . Collections ;
3+ using System . Reflection ;
4+
5+ namespace CLIMediaPlayer . CLI
6+ {
7+ abstract class CLI < T >
8+ {
9+ public static bool CLIRunning = false ;
10+
11+ public static void ShowCommands ( )
12+ {
13+ int maxCmdStrLength = 0 ;
14+ List < ( String , String ) > commandList = scope . ( ) ;
15+
16+ for ( let method in typeof ( T ) . GetMethods ( . Static | . Public ) )
17+ {
18+ CommandAttribute attrib = ? ;
19+ if ( ! ( method . GetCustomAttribute < CommandAttribute > ( ) case . Ok ( out attrib ) ) )
20+ continue;
21+
22+ String str = scope:: . ( ) ;
23+
24+ str . Append ( attrib . mCommand ) ;
25+ str . Append ( " " ) ;
26+
27+ for ( let argAttrib in method. GetCustomAttributes< CommandArgAttribute> ( ) )
28+ {
29+ if ( argAttrib . mOptional )
30+ str . AppendF ( "[{0}] " , argAttrib . mArgName ) ;
31+ else
32+ str. AppendF ( "<{0}> " , argAttrib . mArgName ) ;
33+ }
34+
35+ if ( maxCmdStrLength < str . Length )
36+ maxCmdStrLength = str . Length ;
37+
38+ commandList . Add ( ( str , attrib . mDesc ) ) ;
39+ }
40+
41+ for ( let ( cmdStr , desc ) in commandList)
42+ {
43+ Console . Write ( cmdStr ..PadRight ( maxCmdStrLength + 1 ) ) ;
44+ Console . WriteLine ( desc ) ;
45+ }
46+ }
47+
48+ public static void Run ( )
49+ {
50+ CLIRunning = true ;
51+
52+ runLoop : while ( CLIRunning )
53+ {
54+ Console . Write ( "> " ) ;
55+
56+ String cmd = scope . ( ) ;
57+ if ( Console . ReadLine ( cmd ) case . Err )
58+ {
59+ CLIRunning = false ;
60+ break ;
61+ }
62+
63+ cmd . Trim ( ) ;
64+ var argEnumerator = cmd . Split ( ' ' ) ;
65+
66+ StringView argName ;
67+ if ( ! ( argEnumerator . GetNext ( ) case . Ok ( out argName ) ) )
68+ continue ;
69+
70+ for ( let method in typeof ( T ) . GetMethods ( . Static | . Public ) )
71+ mLoop : {
72+ CommandAttribute attrib = ? ;
73+ if ( ! ( method . GetCustomAttribute < CommandAttribute > ( ) case . Ok ( out attrib ) ) )
74+ continue;
75+
76+ if ( attrib . mCommand != argName )
77+ continue ;
78+
79+ int argCount = 0 ;
80+ for ( let argAttrib in method. GetCustomAttributes< CommandArgAttribute> ( ) )
81+ argCount++ ;
82+
83+ int argIdx = 0 ;
84+ Object [ ] argArray = scope Object[ argCount ] ;
85+
86+ for ( let argAttrib in method . GetCustomAttributes < CommandArgAttribute > ( ) )
87+ {
88+ StringView argStr = . ( ) ;
89+ if ( ! ( argEnumerator . GetNext ( ) case . Ok ( out argStr ) ) && ! argAttrib . mOptional )
90+ {
91+ Console . WriteLine ( "Not enough arguments to run command '{}', expected {} more." , attrib . mCommand , argCount - argIdx ) ;
92+ continue runLoop;
93+ }
94+
95+ Type argType = argAttrib . mArgType ;
96+ bool argNullable = argType. IsNullable;
97+ if ( argNullable )
98+ argType = ( ( SpecializedGenericType ) argType ) . GetGenericArg( 0 ) ;
99+
100+ switch ( argType )
101+ {
102+ case typeof ( TimeSpan ) :
103+ if ( ! argStr . IsEmpty )
104+ {
105+ TimeSpan argTS;
106+ if ( ! ( TimeSpan . Parse ( argStr ) case . Ok ( out argTS ) ) )
107+ {
108+ Console . WriteLine ( "Time is in invalid format." ) ;
109+ continue runLoop;
110+ }
111+
112+ argArray [ argIdx ] = scope : mLoop box argTS;
113+ }
114+ else if ( ! argNullable )
115+ Runtime. FatalError ( "Argument is optional but isn't nullable!" ) ;
116+ case typeof ( bool ) :
117+ if ( ! argStr . IsEmpty )
118+ {
119+ if ( argStr == "y" || argStr == "n" )
120+ argArray[ argIdx ] = scope : mLoop box argStr == "y" ;
121+ else
122+ {
123+ Console . WriteLine ( "Boolean is in invalid format." ) ;
124+ continue runLoop;
125+ }
126+ }
127+ else if ( ! argNullable )
128+ Runtime. FatalError ( "Argument is optional but isn't nullable!" ) ;
129+ case typeof ( double ) :
130+ if ( ! argStr . IsEmpty )
131+ {
132+ double argDouble;
133+ if ( ! ( double . Parse ( argStr ) case . Ok ( out argDouble ) ) )
134+ {
135+ Console . WriteLine ( "Number is in invalid format." ) ;
136+ continue runLoop;
137+ }
138+
139+ argArray [ argIdx ] = scope : mLoop box argDouble;
140+ }
141+ else if ( ! argNullable )
142+ Runtime. FatalError ( "Argument is optional but isn't nullable!" ) ;
143+ case typeof ( String ) :
144+ String fullArgStr = scope: mLoop . ( ) ;
145+ argArray[ argIdx ] = fullArgStr ;
146+
147+ if ( argStr . StartsWith ( '"' ) && argStr . EndsWith ( '"' ) )
148+ argStr = argStr[ 1 ... ^ 2 ] ;
149+
150+ if ( argStr . StartsWith ( '"' ) )
151+ {
152+ fullArgStr. Set ( argStr [ 1 ... ] ) ;
153+ fullArgStr. Append ( ' ' ) ;
154+
155+ while ( true )
156+ {
157+ if ( ! ( argEnumerator . GetNext ( ) case . Ok ( out argStr ) ) )
158+ {
159+ Console. WriteLine ( "Unterminated quoted string." ) ;
160+ continue runLoop;
161+ }
162+
163+ if ( argStr . EndsWith ( '"' ) )
164+ {
165+ fullArgStr. Append ( argStr [ ... ^ 2 ] ) ;
166+ break ;
167+ }
168+
169+ fullArgStr. Append ( argStr ) ;
170+ fullArgStr. Append ( ' ' ) ;
171+ }
172+ }
173+ else
174+ {
175+ fullArgStr. Set ( argStr ) ;
176+ }
177+ }
178+
179+ argIdx++ ;
180+ }
181+
182+ if ( method . Invoke ( null , params argArray ) case . Ok ( var val ) )
183+ val. Dispose ( ) ;
184+
185+ continue runLoop;
186+ }
187+
188+ if ( ! argName . IsEmpty )
189+ Console. WriteLine ( "Unknown command '{}'." , argName ) ;
190+ }
191+ }
192+ }
193+ }
0 commit comments