Skip to content

Commit 4618d4d

Browse files
committed
feat(quartz): Steps starting from x #76
1 parent c1f80c1 commit 4618d4d

File tree

6 files changed

+52
-20
lines changed

6 files changed

+52
-20
lines changed

core/src/__tests__/cron.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { arrayToSegment, cronToSegment } from '@/cron'
2-
import { FieldWrapper } from '@/types'
2+
import { FieldWrapper, type CronFormat } from '@/types'
33
import { genItems } from '@/util'
44
import { describe, expect, it } from 'vitest'
55

6-
const r = (min: number, max: number) => {
7-
return new FieldWrapper({ id: 'fieldId', items: genItems(min, max) })
6+
const r = (min: number, max: number, format: CronFormat = 'crontab') => {
7+
return new FieldWrapper({ id: 'fieldId', items: genItems(min, max) }, { format })
88
}
99

1010
describe('segments', () => {
@@ -23,6 +23,7 @@ describe('segments', () => {
2323
expect(cronToArray('*/10', r(1, 10))).toEqual([1])
2424
expect(cronToArray('2-6/2', r(1, 10))).toEqual([2, 4, 6])
2525
expect(cronToArray('3-5/10', r(1, 10))).toEqual([3])
26+
expect(cronToArray('5/2', r(1, 10, 'quartz'))).toEqual([5, 7, 9])
2627

2728
expect(cronToArray('x', r(0, 59))).toBe(null)
2829
expect(cronToArray('1-60', r(0, 59))).toBe(null)
@@ -32,6 +33,7 @@ describe('segments', () => {
3233
expect(cronToArray('*/90', r(1, 10))).toBe(null)
3334
expect(cronToArray('*/11', r(1, 10))).toBe(null)
3435
expect(cronToArray('2-6/11', r(1, 10))).toBe(null)
36+
expect(cronToArray('5/2', r(1, 10))).toEqual(null)
3537
})
3638

3739
it('arrayToSegment', () => {
@@ -48,5 +50,7 @@ describe('segments', () => {
4850
expect(arrayToCron([5, 12, 19, 26], r(5, 30))).toEqual('*/7')
4951
expect(arrayToCron([0, 5, 10], r(0, 20))).toEqual('0-10/5')
5052
expect(arrayToCron([1, 2, 5, 8, 9, 10], r(1, 10))).toEqual('1-2,5,8-10')
53+
expect(arrayToCron([5, 7, 9], r(1, 10, 'quartz'))).toEqual('5/2')
54+
expect(arrayToCron([5, 7, 9], r(1, 10))).toEqual('5-9/2')
5155
})
5256
})

core/src/components/__tests__/cron-segment.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import { nextTick, ref } from 'vue'
77
import { useCronSegment } from '../cron-segment'
88

99
const f = () => {
10-
const fieldDesc = new FieldWrapper({
11-
id: 'month',
12-
items: defaultItems('en').monthItems,
13-
})
10+
const fieldDesc = new FieldWrapper(
11+
{
12+
id: 'month',
13+
items: defaultItems('en').monthItems,
14+
},
15+
{ format: 'crontab' },
16+
)
1417

1518
const period = { id: 'year', value: [] }
1619

core/src/components/cron-core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export function useCron(options: CronOptions) {
161161
const periodPrefix = ref('')
162162
const periodSuffix = ref('')
163163
const segments = fields.map((f) => {
164-
return useCronSegment({ field: new FieldWrapper(f), l10n, period })
164+
return useCronSegment({ field: new FieldWrapper(f, { format: format }), l10n, period })
165165
})
166166

167167
const segmentMap = new Map(segments.map((s) => [s.id, s]))

core/src/cron.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,20 @@ function _rangeWithStep(n: number, min: number, max: number) {
135135
}
136136
return res
137137
}
138+
139+
function parseRange(s: string, field: FieldWrapper): [number, number] {
140+
if (s === '*') {
141+
return [field.min, field.max]
142+
}
143+
const range = s.split('-').map((s) => parseInt(s))
144+
if (range.length === 1 && field.ctx.format !== 'crontab') {
145+
return [range[0], field.max]
146+
}
147+
return [range[0], range[1]]
148+
}
149+
138150
class StepSegment implements CronSegment {
139-
static re = /^(\*|\d+-\d+)\/\d+$/
151+
static re = /^(\*|\d+(-\d+)?)\/\d+$/
140152

141153
field: FieldWrapper
142154
step: number
@@ -152,14 +164,20 @@ class StepSegment implements CronSegment {
152164

153165
get type() {
154166
const { min, max } = this.field
167+
if (this.field.ctx.format !== 'crontab' && this.start !== min && max - this.end < this.step) {
168+
return FieldPattern.StepFrom
169+
}
155170
if (this.start !== min || max - this.end >= this.step) {
156171
return FieldPattern.RangeStep
157172
}
158173
return FieldPattern.Step
159174
}
160175

161176
toCron() {
162-
if (this.type == FieldPattern.RangeStep) {
177+
if (this.type === FieldPattern.StepFrom) {
178+
return `${this.start}/${this.step}`
179+
}
180+
if (this.type === FieldPattern.RangeStep) {
163181
return `${this.start}-${this.end}/${this.step}`
164182
}
165183
return `*/${this.step}`
@@ -189,9 +207,7 @@ class StepSegment implements CronSegment {
189207
return null
190208
}
191209

192-
const range = str.split('-').map((s) => parseInt(s))
193-
const min = rangeStr == '*' ? field.min : range[0]
194-
const max = rangeStr == '*' ? field.max : range[1]
210+
const [min, max] = parseRange(rangeStr, field)
195211

196212
if (_rangeWithStep(step, min, max).length == 0) {
197213
return null

core/src/locale/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ const locale: Localization = {
1111
range: { text: '{{start.text}}-{{end.text}}' },
1212
step: { text: 'every {{step.value}}' },
1313
rangeStep: { text: '{{start.text}}-{{end.text}}/{{step.value}}' },
14+
stepFrom: { text: '{{start.text}}/{{step.value}}' },
1415
},
1516
month: {
1617
'*': { prefix: 'in' },
1718
value: { text: '{{value.alt}}' },
1819
range: { text: '{{start.alt}}-{{end.alt}}' },
1920
rangeStep: { text: '{{start.alt}}-{{end.alt}}/{{step.value}}' },
21+
stepFrom: { text: '{{start.alt}}/{{step.value}}' },
2022
},
2123
day: {
2224
'*': { prefix: 'on' },
@@ -30,6 +32,7 @@ const locale: Localization = {
3032
value: { text: '{{value.alt}}' },
3133
range: { text: '{{start.alt}}-{{end.alt}}' },
3234
rangeStep: { text: '{{start.alt}}-{{end.alt}}/{{step.value}}' },
35+
stepFrom: { text: '{{start.alt}}/{{step.value}}' },
3336
noSpecific: {
3437
text: 'no specific day of the week',
3538
},

core/src/types.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ export type SegmentFromArray = (arr: number[], field: FieldWrapper) => CronSegme
1515
export type SegmentFromString = (str: string, field: FieldWrapper) => CronSegment | null
1616

1717
export enum FieldPattern {
18-
Any = 'any',
19-
Value = 'value',
20-
Range = 'range',
21-
Step = 'step',
22-
RangeStep = 'rangeStep',
18+
Any = 'any', // *
19+
Value = 'value', // a
20+
Range = 'range', // a-b
21+
Step = 'step', // */x
22+
StepFrom = `stepFrom`, // a/x
23+
RangeStep = 'rangeStep', // a-b/x
2324
Combined = 'combined',
24-
NoSpecific = 'noSpecific',
25+
NoSpecific = 'noSpecific', // ?
2526
}
2627

2728
export enum TextPosition {
@@ -61,12 +62,17 @@ export interface Period {
6162
text?: string
6263
}
6364

65+
interface FieldContext {
66+
format: CronFormat
67+
}
6468
export class FieldWrapper {
6569
field: Field
6670
itemMap: Record<number, FieldItem>
71+
ctx: FieldContext
6772

68-
constructor(field: Field) {
73+
constructor(field: Field, ctx: FieldContext) {
6974
this.field = field
75+
this.ctx = ctx
7076

7177
this.itemMap = this.field.items.reduce(
7278
(acc, item) => {

0 commit comments

Comments
 (0)