@@ -12,12 +12,11 @@ const isA = Array.isArray;
1212 * @property {OptKit } [set] Array of options to set the variable value
1313 * @property {OptKit } [rst] Array of options to reset the variable value
1414 *
15- * @typedef {OptDef | OptDef[] } HaltKit Halt options, identical to `OptKit`, for now...
16- * @typedef {{opt: OptStr, key: VarKey} } HaltRes
17- * @typedef {Record<VarKey, VarKit | HaltKit> } KeyKitMap
15+ * @typedef {OptDef | OptDef[] } ExitKit Exit options, identical to `OptKit` for now
16+ * @typedef {Record<VarKey, VarKit | ExitKit> } KeyKitMap
1817 * @typedef {Record<VarKey, VarVal> } KeyValMap
1918 *
20- * @callback CanQuit
19+ * @callback IsFatal
2120 * @param {{msg: string, i: number, opt: OptStr, key?: VarKey, val?: VarVal } } err
2221 * @returns {boolean } Whether the parsing should continue (false) or quit (true)
2322 * @typedef {Record<OptStr, VarKey> } OptKeyMap internal type
@@ -26,150 +25,118 @@ const isA = Array.isArray;
2625const god = ok => typeof ok === 'string' ? [ ok ] : isA ( ok ) ? ok : [ ] ;
2726/**
2827 * Command line argument parser function
29- * @param {string[] } argv Command line arguments array
30- * @param {number } i Index of current argument being processed
28+ * @param {string[] } argv Command line arguments array, e.g. `process.argv`
29+ * @param {number } i Index of current argument being processed, e.g. `2`
3130 * @param {KeyKitMap } req Options structure definition
3231 * @param {KeyValMap } res Object to store parsed results
33- * @param {CanQuit } err Error handler function
34- * @returns {{ i: number, halt ?: HaltRes } }
32+ * @param {IsFatal } err Error handler function, return true to quit parsing
33+ * @returns {{ i: number, opt: OptStr, key ?: VarKey, exit: boolean } }
3534 * @example
3635 */
3736export default function parse ( argv , i , req , res , err ) {
38- /** @type {OptKeyMap } options to set var */
39- const set_ = { } ;
40- /** @type {OptKeyMap } options to reset var */
41- const rst_ = { } ;
42- /** @type {OptKeyMap } options to halt parse */
43- const hlt_ = { } ;
4437 /** @type {OptStr } option */
4538 let opt = '' ;
46- /** @type {OptStr } option of the extension */
47- let ext = '' ;
4839 /** @type {VarKey | undefined } key */
4940 let key ;
50- /** @type {VarKey | undefined } key to set the universe variable */
51- let suv ;
52- /** @type {boolean } set universe variable to halt (?) */
53- let sun = false ;
54- // methodsze
55- /** @param {string } msg @param {VarVal } [val] */
56- const ask = ( msg , val ) => err ( { msg, i, opt, key, val} ) ;
57- // below: assert(key != null)
5841 /** @param {VarVal } val */
5942 const set = val => {
6043 const cur = res [ key ] ;
6144 if ( isA ( cur ) ) cur . push ( val ) ; else res [ key ] = val ;
62- } ;
63- const rst = ( ) => {
45+ } , rst = ( ) => {
6446 const def = req [ key ] . def ;
6547 res [ key ] = isA ( def ) ? def . slice ( ) : def ;
66- } ;
67- const noB = ( ) => {
48+ } , noB = ( ) => {
6849 const def = req [ key ] . def ;
6950 if ( typeof def === 'boolean' ) {
7051 res [ key ] = ! def ;
7152 return false ;
7253 } return true ;
73- } ;
74- /** @param {string } s */
75- const one = s => {
76- if ( key = set_ [ opt = s ] ) {
77- if ( noB ( ) ) ext = opt ;
78- } else if ( key = rst_ [ opt ] ) rst ( ) ;
79- else return ask ( 'invalid option' ) ;
80- return false ;
81- } ;
54+ } , ask = ( msg , val ) => err ( { msg, i, opt, key, val} ) ;
8255 // prepare
56+ /** @type {OptKeyMap } */
57+ const set_ = { } , rst_ = { } , exit_ = { } ;
58+ /** @type {VarKey | undefined } */
59+ let _key , _exit = false ;
8360 for ( key in req ) {
8461 const vk = req [ key ] ;
85- H: { let hk ;
62+ H: { let xk ; // stricter than god()
8663 switch ( typeof vk ) {
8764 case 'object' :
88- if ( vk == null ) hk = [ key ] ;
89- else if ( isA ( vk ) ) hk = vk ;
65+ if ( vk == null ) xk = [ key ] ;
66+ else if ( isA ( vk ) ) xk = vk ;
9067 else break H; break ;
91- case 'string' : hk = [ vk ] ; break ;
68+ case 'string' : xk = [ vk ] ; break ;
9269 default : continue ; }
93- for ( const o of hk ) if ( o !== '--' ) hlt_ [ o || key ] = key ; else suv = key , sun = true ; // lol
70+ for ( const o of xk ) if ( o !== '--' ) exit_ [ o || key ] = key ; else _key = key , _exit = true ;
9471 continue ; }
9572 const def = vk . def ;
96- res [ key ] = isA ( def ) ? def . slice ( ) : def ; // just rst() with known `def`
97- for ( const o of god ( vk . set ) ) if ( o !== '--' ) set_ [ o || key ] = key ; else suv = key , sun = false ;
98- for ( const o of god ( vk . rst ) ) if ( o !== '--' ) rst_ [ o || key ] = key ; // ?? wanna reset around?
73+ res [ key ] = isA ( def ) ? def . slice ( ) : def ;
74+ for ( const o of god ( vk . set ) ) if ( o !== '--' ) set_ [ o || key ] = key ; else _key = key , _exit = false ;
75+ for ( const o of god ( vk . rst ) ) if ( o !== '--' ) rst_ [ o || key ] = key ;
9976 }
10077 // process
101- /** @type {HaltRes } */
102- let halt = null ;
78+ let ext = '' , exit = '' ;
10379 I: for ( ; i < argv . length ; ++ i ) {
10480 const s = argv [ i ] ;
105- // extension ~ Just one more thing
106- if ( ext ) { // fact(const val = s)
107- ext = '' ; // assert(opt === ext)
108- if ( key ) { // assert(key === set_[opt])
109- set ( s ) ;
110- continue ;
111- }
81+ // extension ~ ASSERT key = set_[opt = ext]
82+ if ( ext ) { ext = '' ;
83+ if ( key ) { set ( s ) ; continue ; }
11284 if ( ask ( 'invalid option' , s ) ) break ;
11385 }
114- // halt ~ Basic so that `i` is usable for resuming parsing
115- if ( key = hlt_ [ opt = s ] ) { ++ i ; halt = { opt, key} ; break ; }
116- // abc
117- if ( s . length < 2 || s [ 0 ] !== '-' ) {
118- if ( key = set_ [ opt = s ] ) {
119- if ( noB ( ) ) ext = opt ;
120- } else if ( key = rst_ [ opt ] ) rst ( ) ;
121- else if ( key = suv ) if ( sun ) { ++ i ; halt = { opt, key} ; break ; } else set ( s ) ;
86+ if ( s . length < 2 || s [ 0 ] !== '-' ) { // abc
87+ if ( key = set_ [ opt = s ] ) { if ( noB ( ) ) ext = opt ; }
88+ else if ( key = rst_ [ opt ] ) rst ( ) ;
89+ else if ( key = exit_ [ opt ] ) { exit = key ; break ; }
90+ else if ( key = _key ) if ( _exit ) { exit = key ; break ; } else set ( s ) ;
12291 else if ( ask ( 'invalid option' , s ) ) break ;
123- continue ;
124- }
125- // -abc
126- if ( s [ 1 ] !== '-' ) {
127- // -ab ~ no extension
92+ } else if ( s [ 1 ] !== '-' ) { // -abc
12893 const J = s . length - 1 ;
12994 for ( let j = 1 ; j < J ; ++ j ) {
95+ // -ab ~ no extension, no universe, no exit
13096 opt = '-' + s [ j ] ;
131- if ( key = set_ [ opt ] ) {
132- if ( noB ( ) ) {
133- set ( s . slice ( j + 1 ) ) ;
134- continue I;
135- }
136- } else if ( key = rst_ [ opt ] ) rst ( ) ;
97+ if ( key = set_ [ opt ] ) { if ( noB ( ) ) { set ( s . slice ( j + 1 ) ) ; continue I; } }
98+ else if ( key = rst_ [ opt ] ) rst ( ) ;
99+ else if ( key = exit_ [ opt ] ) { if ( ask ( 'cannot exit inside an argument' ) ) break I; }
137100 else if ( ask ( 'invalid option' ) ) break I;
138101 }
139- // -c ~ not universe
140- if ( one ( '-' + s [ J ] ) ) break ;
141- continue ;
142- }
143- // --abc
144- if ( s . length > 2 ) {
102+ // -c ~ no universe, can exit
103+ opt = '-' + s [ J ] ;
104+ if ( key = set_ [ opt ] ) { if ( noB ( ) ) ext = opt ; }
105+ else if ( key = rst_ [ opt ] ) rst ( ) ;
106+ else if ( key = exit_ [ opt ] ) { exit = key ; break ; }
107+ else if ( ask ( 'invalid option' ) ) break ;
108+ } else if ( s . length > 2 ) { // --opt
145109 const k = s . indexOf ( '=' ) ;
146- // --opt ~ not universe
147- if ( k < 0 ) if ( one ( s ) ) break ; else continue ;
148- // --opt=val ~ explicit assignment
110+ if ( k < 0 ) {
111+ // --opt ...
112+ if ( key = set_ [ opt = s ] ) { if ( noB ( ) ) ext = opt ; }
113+ else if ( key = rst_ [ opt ] ) rst ( ) ;
114+ else if ( key = exit_ [ opt ] ) { exit = key ; break ; }
115+ else if ( ask ( 'invalid option' ) ) break ;
116+ continue ;
117+ }
118+ // --opt=val
149119 opt = s . slice ( 0 , k ) ;
150- const val = s . slice ( k + 1 ) ;
151- if ( key = set_ [ opt ] ) {
152- if ( typeof res [ key ] === 'boolean' ) {
153- if ( ask ( 'Cannot assign a value to a boolean-type option' , val ) ) break ;
154- } else set ( val ) ;
155- } else if ( key = rst_ [ opt ] ) {
156- if ( ask ( 'Cannot assign a value to a reset-type option' , val ) ) break ;
157- } else {
158- if ( ask ( 'invalid option' , val ) ) break ;
120+ const v = s . slice ( k + 1 ) ; let t ;
121+ if ( key = set_ [ opt ] )
122+ switch ( t = typeof res [ key ] ) {
123+ case 'boolean' : break ;
124+ default : set ( v ) ; continue ; }
125+ else if ( key = rst_ [ opt ] ) t = 'reset' ;
126+ else if ( key = exit_ [ opt ] ) t = 'exit' ;
127+ else if ( ask ( 'invalid option' , v ) ) break ; else continue ;
128+ if ( ask ( `Cannot assign a value to a ${ t } option` , v ) ) break ;
129+ } else { opt = '--' ;
130+ if ( key = _key ) {
131+ if ( _exit ) { exit = key ; break ; }
132+ const a = res [ key ] , l = argv . length ; ++ i ;
133+ if ( isA ( a ) ) while ( i < l ) a . push ( argv [ i ++ ] ) ;
134+ else if ( i < l ) res [ key ] = argv [ ( i = l ) - 1 ] ;
135+ break ;
159136 }
160- continue ;
161- }
162- // -- ~ collect all
163- opt = '--' ;
164- if ( key = suv ) { ++ i ;
165- if ( sun ) { halt = { opt, key} ; break ; }
166- const cur = res [ key ] , len = argv . length ;
167- if ( isA ( cur ) ) while ( i < len ) cur . push ( argv [ i ++ ] ) ;
168- else if ( i < len ) res [ key ] = argv [ ( i = len ) - 1 ] ;
169- break ;
137+ if ( ask ( 'unexpected argument' ) ) break ;
170138 }
171- if ( ask ( 'unexpected argument' ) ) break ;
172139 }
173- if ( ext ) ask ( 'This option requires an argument' ) ; // assertion same as above
174- return { i, halt } ;
140+ if ( ext ) ask ( 'This option requires an argument' ) ;
141+ return { i, exit } ;
175142} ;
0 commit comments