Skip to content

Commit

Permalink
Add support for 'mondays' like syntax without needing to specify a fr…
Browse files Browse the repository at this point in the history
…equency or occurence token
  • Loading branch information
rautio committed Nov 7, 2023
1 parent 71b7c7f commit 35c5fb2
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 68 deletions.
11 changes: 10 additions & 1 deletion __tests__/parseText/suites/smoke.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,13 @@ in 2 weeks
0 9 18 * *

in 5 months
0 9 4 7 *
0 9 4 7 *

Thursdays
0 9 * * 4

mon
0 9 * * 1

weekdays
0 9 * * 1-5
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "crontext",
"version": "0.2.10",
"version": "0.2.11",
"description": "Simple utility for parsing human text into a cron schedule.",
"files": [
"lib"
Expand Down
53 changes: 53 additions & 0 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Token } from '../tokenize';
import type { Options } from '../options';
import { rules } from './rules';

export type Crontext = {
minutes: string;
hour: string;
dayOfMonth: string;
dayOfWeek: string;
month: string;
repeat: boolean;
};

export const DEFAULT = '*';
export const INIT = '_'; // Used to know whether the value has been set at all.
export const DEFAULT_DAY_MINUTES = '0';
export const DEFAULT_DAY_HOURS = '9';

const defaultParsed: Crontext = {
minutes: INIT,
hour: INIT,
dayOfMonth: INIT,
dayOfWeek: INIT,
month: INIT,
repeat: false,
};
export const parser = (tokens: Token[], options: Options): Crontext => {
let crontext = { ...defaultParsed };
// Iterate all tokens
for (let t = 0; t < tokens.length; t++) {
// Check if any of the rules match the given token and forward lookups.
for (let r = 0; r < rules.length; r++) {
const rule = rules[r];
const ruleLen = rule.match.length;
if (ruleLen <= tokens.length - t) {
const sub = tokens.slice(t, t + ruleLen);
let isMatch = true;
for (let s = 0; s < sub.length; s++) {
if (sub[s].type !== rule.match[s]) {
isMatch = false;
}
}
if (isMatch) {
t = t + sub.length - 1;
crontext = rule.update(crontext, sub, options);
}
}
}
}
return crontext;
};

export default parser;
87 changes: 21 additions & 66 deletions src/parser.ts → src/parser/rules.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import type { Token } from './tokenize';
import { TokenType } from './tokens';
import { testRepeat as freqTestRepeat } from './tokens/frequency';
import { getNumber } from './tokens/number';
import { getTime } from './tokens/clock';
import { getDayOfWeek, pluralDayRegexOptions } from './tokens/day';
import type { Options } from './options';

export type Crontext = {
minutes: string;
hour: string;
dayOfMonth: string;
dayOfWeek: string;
month: string;
repeat: boolean;
};

export const DEFAULT = '*';
export const INIT = '_'; // Used to know whether the value has been set at all.
export const DEFAULT_DAY_MINUTES = '0';
export const DEFAULT_DAY_HOURS = '9';
import type { Token } from '../tokenize';
import { TokenType } from '../tokens';
import { testRepeat as freqTestRepeat } from '../tokens/frequency';
import { getNumber } from '../tokens/number';
import { getTime } from '../tokens/clock';
import { getDayOfWeek, pluralDayRegexOptions } from '../tokens/day';
import type { Options } from '../options';
import { INIT, DEFAULT } from './index';
import type { Crontext } from './index';

const {
FREQUENCY,
Expand All @@ -32,15 +20,6 @@ const {
RELATIVE_DAY,
} = TokenType;

const defaultParsed: Crontext = {
minutes: INIT,
hour: INIT,
dayOfMonth: INIT,
dayOfWeek: INIT,
month: INIT,
repeat: false,
};

export const updateDay = (
crontext: Crontext,
tokens: Token[],
Expand All @@ -51,15 +30,15 @@ export const updateDay = (
if (crontext.minutes === INIT) crontext.minutes = options.defaultMinute;
if (crontext.hour === INIT) crontext.hour = options.defaultHour;
const re = new RegExp(pluralDayRegexOptions);
// Plural 'on mondays' means its repeating
if (
tokens.length >= 2 &&
tokens[0].value === 'on' &&
re.test(tokens[1].value)
) {
crontext.repeat = true;
const dayToken = tokens.find(t => t.type === DAY);
let dayOfWeek = '*';
if (dayToken) {
// Plural 'on mondays' means its repeating
if (re.test(dayToken.value)) {
crontext.repeat = true;
}
dayOfWeek = getDayOfWeek(dayToken.value);
}
const dayOfWeek = getDayOfWeek(tokens[1].value);
return { ...crontext, dayOfWeek };
};

Expand Down Expand Up @@ -173,6 +152,10 @@ export const rules = [
match: [FREQUENCY, DAY],
update: updateDay,
},
{
match: [DAY],
update: updateDay,
},
{
match: [FREQUENCY, DAYS],
update: (
Expand Down Expand Up @@ -219,31 +202,3 @@ export const rules = [
},
},
];

export const parser = (tokens: Token[], options: Options): Crontext => {
let crontext = { ...defaultParsed };
// Iterate all tokens
for (let t = 0; t < tokens.length; t++) {
// Check if any of the rules match the given token and forward lookups.
for (let r = 0; r < rules.length; r++) {
const rule = rules[r];
const ruleLen = rule.match.length;
if (ruleLen <= tokens.length - t) {
const sub = tokens.slice(t, t + ruleLen);
let isMatch = true;
for (let s = 0; s < sub.length; s++) {
if (sub[s].type !== rule.match[s]) {
isMatch = false;
}
}
if (isMatch) {
t = t + sub.length - 1;
crontext = rule.update(crontext, sub, options);
}
}
}
}
return crontext;
};

export default parser;

0 comments on commit 35c5fb2

Please sign in to comment.