1
- /**
1
+ /*
2
2
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3
3
*
4
4
* This program is free software: you can redistribute it and/or modify
23
23
* in the event they are changed in cylc-flow.
24
24
*/
25
25
26
- /* eslint-disable */
26
+ /* eslint-disable no-useless-escape */
27
27
28
28
const UNIVERSAL_ID = new RegExp ( `
29
29
(?=.)
@@ -121,10 +121,24 @@ const RELATIVE_ID = new RegExp(`
121
121
$
122
122
` . replace ( / [ \s \n \r ] / g, '' ) )
123
123
124
- /* eslint-enable */
124
+ /* eslint-enable no-useless-escape */
125
125
126
- function detokenise ( tokens , workflow = true , relative = true ) {
127
- let parts = [ ]
126
+ interface TokenFields {
127
+ user ?: string
128
+ workflow ?: string
129
+ cycle ?: string
130
+ task ?: string
131
+ job ?: string
132
+ }
133
+
134
+ type TokenKey = keyof TokenFields
135
+
136
+ type TokenTree = [ TokenKey | 'workflow-part' , string , Tokens ] [ ]
137
+
138
+ function detokenise (
139
+ tokens : Tokens , workflow : boolean = true , relative : boolean = true
140
+ ) : string {
141
+ let parts : string [ ] = [ ]
128
142
let ret = ''
129
143
130
144
if ( workflow ) {
@@ -159,7 +173,7 @@ function detokenise (tokens, workflow = true, relative = true) {
159
173
return ret
160
174
}
161
175
162
- class Tokens {
176
+ export class Tokens implements TokenFields {
163
177
/* Represents a Cylc UID.
164
178
*
165
179
* Provides the interfaces for parsing to and from string IDs.
@@ -184,76 +198,66 @@ class Tokens {
184
198
* tokens.relativeID // the task part of the ID (the bit after the //)
185
199
*/
186
200
187
- static KEYS = [ 'user' , 'workflow' , 'cycle' , 'task' , 'job' ]
201
+ static KEYS : readonly TokenKey [ ] = [ 'user' , 'workflow' , 'cycle' , 'task' , 'job' ]
202
+ static KEYS_LO_TO_HI : readonly TokenKey [ ] = [ ...Tokens . KEYS ] . reverse ( )
188
203
189
- constructor ( id , relative = false ) {
190
- let match
191
- let user
192
- let workflow
193
- let cycle
194
- let task
195
- let job
204
+ user ?: string
205
+ workflow ?: string
206
+ cycle ?: string
207
+ task ?: string
208
+ job ?: string
196
209
197
- if ( id === null ) {
210
+ id ! : string
211
+ workflowID ! : string
212
+ relativeID ! : string
213
+ namespace ?: string
214
+ edge ?: [ Tokens , Tokens ]
215
+
216
+ constructor ( id : string , relative : boolean = false ) {
217
+ if ( id == null ) {
198
218
throw new Error ( `Invalid ID ${ id } ` )
199
219
}
200
220
221
+ let match : RegExpMatchArray | null = null
222
+
201
223
// try to match relative ID (the leading // is implicit)
202
224
if ( relative ) {
203
225
match = `//${ id } ` . match ( RELATIVE_ID )
204
226
if ( match ) {
205
- user = undefined
206
- workflow = undefined
207
- cycle = match [ 1 ]
208
- task = match [ 3 ]
209
- job = match [ 5 ]
227
+ this . cycle = match [ 1 ]
228
+ this . task = match [ 3 ]
229
+ this . job = match [ 5 ]
210
230
}
211
231
}
212
232
213
233
// try to match full id (or // prefixed relative id)
214
234
if ( ! match ) {
215
235
match = id . match ( UNIVERSAL_ID )
216
236
if ( match ) {
217
- user = match [ 1 ]
218
- workflow = match [ 3 ]
219
- cycle = match [ 5 ]
220
- task = match [ 7 ]
221
- job = match [ 9 ]
237
+ this . user = match [ 1 ]
238
+ this . workflow = match [ 3 ]
239
+ this . cycle = match [ 5 ]
240
+ this . task = match [ 7 ]
241
+ this . job = match [ 9 ]
242
+ } else {
243
+ throw new Error ( `Invalid ID ${ id } ` )
222
244
}
223
245
}
224
246
225
- if ( ! match ) {
226
- throw new Error ( `Invalid ID ${ id } ` )
227
- }
228
-
229
- // UID tokens
230
- this . user = user
231
- this . workflow = workflow
232
- this . cycle = cycle
233
- this . task = task
234
- this . job = job
235
-
236
- // derived properties
237
- this . namespace = undefined
238
- this . edge = undefined
239
- this . id = undefined
240
- this . workflowID = undefined
241
- this . relativeID = undefined
242
-
243
- // update derived properties
247
+ // set derived properties
244
248
this . compute ( )
245
249
}
246
250
247
- compute ( ) {
251
+ protected compute ( ) : void {
248
252
this . id = detokenise ( this )
249
253
250
- if ( this . cycle && this . cycle . startsWith ( '$namespace|' ) ) {
254
+ if ( this . cycle ? .startsWith ( '$namespace|' ) ) {
251
255
// this is a namespace definition
252
256
this . namespace = this . cycle . replace ( '$namespace|' , '' )
253
257
this . cycle = undefined
254
258
this . task = undefined
255
259
this . job = undefined
256
- } else if ( this . cycle && this . cycle . startsWith ( '$edge|' ) ) {
260
+ } else if ( this . cycle ? .startsWith ( '$edge|' ) ) {
257
261
// this is an edge definition
258
262
const [ left , right ] = this . id . replace ( / .* \$ e d g e \| / , '' ) . split ( '|' )
259
263
this . edge = [ new Tokens ( left , true ) , new Tokens ( right , true ) ]
@@ -266,70 +270,61 @@ class Tokens {
266
270
this . relativeID = detokenise ( this , false , true )
267
271
}
268
272
269
- set ( fields ) {
270
- for ( const [ key , value ] of Object . entries ( fields ) ) {
271
- if ( Tokens . KEYS . indexOf ( key ) === - 1 ) {
272
- throw new Error ( `Invalid key: ${ key } ` )
273
- }
274
- if ( typeof value !== 'string' && typeof value !== 'undefined' ) {
275
- throw new Error ( `Invalid type for value: ${ value } ` )
276
- }
277
- this [ key ] = value
278
- }
279
- this . compute ( )
280
- }
281
-
282
- clone ( fields = null ) {
273
+ clone ( fields ?: TokenFields ) : Tokens {
283
274
const ret = Object . create (
284
275
Object . getPrototypeOf ( this ) ,
285
276
Object . getOwnPropertyDescriptors ( this )
286
277
)
287
278
if ( fields ) {
288
- ret . set ( fields )
279
+ for ( const [ key , value ] of Object . entries ( fields ) ) {
280
+ if ( Tokens . KEYS . indexOf ( key as TokenKey ) === - 1 ) {
281
+ throw new Error ( `Invalid key: ${ key } ` )
282
+ }
283
+ if ( typeof value !== 'string' && typeof value !== 'undefined' ) {
284
+ throw new Error ( `Invalid type for value: ${ value } ` )
285
+ }
286
+ ret [ key ] = value
287
+ }
288
+ ret . compute ( )
289
289
}
290
290
return ret
291
291
}
292
292
293
- workflowHierarchy ( ) {
294
- const hier = [ ]
295
- const tokensList = [ ]
296
- let tokens
293
+ workflowHierarchy ( ) : [ string , Tokens ] [ ] {
294
+ if ( ! this . workflow ) {
295
+ throw new Error ( 'Cannot get workflow hierarchy for a relative ID' )
296
+ }
297
+ const hier : string [ ] = [ ]
298
+ const tokensList : [ string , Tokens ] [ ] = [ ]
297
299
for ( const part of this . workflow . split ( '/' ) ) {
298
300
// for each level of the hierarchy
299
301
hier . push ( part )
300
302
// copy these tokens
301
- tokens = this . clone ( )
302
- // amending the workflow ID to match its level in the hierarchy
303
- tokens . set ( {
303
+ const tokens = this . clone ( {
304
+ // amending the workflow ID to match its level in the hierarchy
304
305
workflow : hier . join ( '/' ) ,
305
306
// wipe the relative tokens in-case they were set
306
307
cycle : undefined ,
307
308
task : undefined ,
308
- job : undefined
309
+ job : undefined ,
309
310
} )
310
311
tokensList . push ( [ part , tokens ] )
311
312
}
312
313
return tokensList
313
314
}
314
315
315
- lowestToken ( ) {
316
- let key
317
- for ( let ind = Tokens . KEYS . length ; ind >= 0 ; ind -- ) {
318
- key = Tokens . KEYS [ ind ]
319
- if ( this [ key ] ) {
320
- return key
321
- }
322
- }
316
+ lowestToken ( ) : TokenKey {
317
+ return Tokens . KEYS_LO_TO_HI . find ( key => this [ key ] ) as TokenKey
323
318
}
324
319
325
- tree ( ) {
326
- const ret = [ ]
320
+ tree ( ) : TokenTree {
321
+ const ret : TokenTree = [ ]
327
322
if ( this . user ) {
328
323
let tokens = new Tokens ( `~${ this . user } ` )
329
324
ret . push ( [ 'user' , this . user , tokens ] )
330
325
if ( this . workflow ) {
331
326
const parts = this . workflow . split ( '/' )
332
- const last = parts . pop ( )
327
+ const last = parts . pop ( ) as string
333
328
for ( const part of parts ) {
334
329
if ( ! tokens . workflow ) {
335
330
tokens = tokens . clone ( { workflow : part } )
@@ -361,5 +356,3 @@ class Tokens {
361
356
return ret
362
357
}
363
358
}
364
-
365
- export { Tokens }
0 commit comments