Skip to content

Commit 345c6d9

Browse files
committed
Convert uid to TypeScript
1 parent 432b7b3 commit 345c6d9

File tree

4 files changed

+95
-127
lines changed

4 files changed

+95
-127
lines changed

src/components/cylc/common/filter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ interface Node {
3030
* Return true if the node ID matches the given ID, or if no ID is given.
3131
*/
3232
export function matchID (node: Node, id?: string): boolean {
33-
return !id?.trim() || node.tokens.relativeID!.includes(id)
33+
return !id?.trim() || node.tokens.relativeID.includes(id)
3434
}
3535

3636
/**

src/components/cylc/gscan/filters.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ interface WorkflowNode {
2525
tokens: Tokens
2626
}
2727

28-
export function filterByName (workflow: WorkflowNode, name: string): boolean {
29-
return !name || workflow.tokens.workflow.toLowerCase().includes(name.toLowerCase())
28+
export function filterByName (workflow: WorkflowNode, name?: string): boolean {
29+
return !name || workflow.tokens.workflow!.toLowerCase().includes(name.toLowerCase())
3030
}
3131

3232
/**
3333
* @private
3434
* @param stateTotals - object with the keys being states, and values the count
3535
*/
36-
function getWorkflowStates (stateTotals: Record<string, number>): string[] {
36+
function getWorkflowStates (stateTotals?: Record<string, number>): string[] {
3737
return !stateTotals
3838
? []
3939
: Object.keys(stateTotals).filter((state) => stateTotals[state] > 0)

src/utils/uid.js renamed to src/utils/uid.ts

Lines changed: 76 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/*
22
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
33
*
44
* This program is free software: you can redistribute it and/or modify
@@ -23,7 +23,7 @@
2323
* in the event they are changed in cylc-flow.
2424
*/
2525

26-
/* eslint-disable */
26+
/* eslint-disable no-useless-escape */
2727

2828
const UNIVERSAL_ID = new RegExp(`
2929
(?=.)
@@ -121,10 +121,24 @@ const RELATIVE_ID = new RegExp(`
121121
$
122122
`.replace(/[\s\n\r]/g, ''))
123123

124-
/* eslint-enable */
124+
/* eslint-enable no-useless-escape */
125125

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[] = []
128142
let ret = ''
129143

130144
if (workflow) {
@@ -159,7 +173,7 @@ function detokenise (tokens, workflow = true, relative = true) {
159173
return ret
160174
}
161175

162-
class Tokens {
176+
export class Tokens implements TokenFields {
163177
/* Represents a Cylc UID.
164178
*
165179
* Provides the interfaces for parsing to and from string IDs.
@@ -184,76 +198,66 @@ class Tokens {
184198
* tokens.relativeID // the task part of the ID (the bit after the //)
185199
*/
186200

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()
188203

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
196209

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) {
198218
throw new Error(`Invalid ID ${id}`)
199219
}
200220

221+
let match: RegExpMatchArray | null = null
222+
201223
// try to match relative ID (the leading // is implicit)
202224
if (relative) {
203225
match = `//${id}`.match(RELATIVE_ID)
204226
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]
210230
}
211231
}
212232

213233
// try to match full id (or // prefixed relative id)
214234
if (!match) {
215235
match = id.match(UNIVERSAL_ID)
216236
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}`)
222244
}
223245
}
224246

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
244248
this.compute()
245249
}
246250

247-
compute () {
251+
protected compute (): void {
248252
this.id = detokenise(this)
249253

250-
if (this.cycle && this.cycle.startsWith('$namespace|')) {
254+
if (this.cycle?.startsWith('$namespace|')) {
251255
// this is a namespace definition
252256
this.namespace = this.cycle.replace('$namespace|', '')
253257
this.cycle = undefined
254258
this.task = undefined
255259
this.job = undefined
256-
} else if (this.cycle && this.cycle.startsWith('$edge|')) {
260+
} else if (this.cycle?.startsWith('$edge|')) {
257261
// this is an edge definition
258262
const [left, right] = this.id.replace(/.*\$edge\|/, '').split('|')
259263
this.edge = [new Tokens(left, true), new Tokens(right, true)]
@@ -266,70 +270,61 @@ class Tokens {
266270
this.relativeID = detokenise(this, false, true)
267271
}
268272

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 {
283274
const ret = Object.create(
284275
Object.getPrototypeOf(this),
285276
Object.getOwnPropertyDescriptors(this)
286277
)
287278
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()
289289
}
290290
return ret
291291
}
292292

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][] = []
297299
for (const part of this.workflow.split('/')) {
298300
// for each level of the hierarchy
299301
hier.push(part)
300302
// 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
304305
workflow: hier.join('/'),
305306
// wipe the relative tokens in-case they were set
306307
cycle: undefined,
307308
task: undefined,
308-
job: undefined
309+
job: undefined,
309310
})
310311
tokensList.push([part, tokens])
311312
}
312313
return tokensList
313314
}
314315

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
323318
}
324319

325-
tree () {
326-
const ret = []
320+
tree (): TokenTree {
321+
const ret: TokenTree = []
327322
if (this.user) {
328323
let tokens = new Tokens(`~${this.user}`)
329324
ret.push(['user', this.user, tokens])
330325
if (this.workflow) {
331326
const parts = this.workflow.split('/')
332-
const last = parts.pop()
327+
const last = parts.pop() as string
333328
for (const part of parts) {
334329
if (!tokens.workflow) {
335330
tokens = tokens.clone({ workflow: part })
@@ -361,5 +356,3 @@ class Tokens {
361356
return ret
362357
}
363358
}
364-
365-
export { Tokens }

0 commit comments

Comments
 (0)