Skip to content

Commit b28e76a

Browse files
Merge pull request #49 from itscrystalline/custom-if-expr
feat: custom time predicates & shell conditions
2 parents 56c4624 + d09e459 commit b28e76a

File tree

6 files changed

+612
-164
lines changed

6 files changed

+612
-164
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ clap = { version = "4.5.37", features = ["derive"] }
2626
clap_derive = { version = "4.0.0-rc.1" }
2727
colored = "3"
2828
dirs = "6.0.0"
29+
evalexpr = "12.0.2"
2930
fastrand = "2.3.0"
3031
map-macro = { version = "0.3.0", features = ["std"] }
3132
serde = { version = "1.0.219", features = ["derive"] }

occasions.schema.json

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,13 @@
1212
"type": "array",
1313
"items": {
1414
"type": "object",
15-
"required": [
16-
"time"
17-
],
1815
"properties": {
1916
"message": {
2017
"description": "The message to show when the configured date pattern matches.",
2118
"type": "string"
2219
},
2320
"command": {
24-
"description": "The inputs to pass to the spawned shell. This could either be a simple shell command, or a script that you pass into a shell or interpreter.",
21+
"description": "The inputs to pass to the spawned shell. This could either be a simple shell command, or a script that you pass into a shell or interpreter.\nThe following environment variables are available to you:\n - `DAY_OF_WEEK`: The name of day of the week. (ex. \"Tuesday\")\n - `DAY_IN_WEEK`: The number of days after the week has started. Week boundary is set in `week_start_day`.\n - `DAY_OF_MONTH`: The day of the month.\n - `WEEK`: The week number in the year.\n - `MONTH`: The month number.\n - `YEAR`: The year in your system's default locale's year format, usually AD.",
2522
"type": "object",
2623
"required": [
2724
"run"
@@ -45,7 +42,7 @@
4542
}
4643
},
4744
"time": {
48-
"description": "The date pattern to match.",
45+
"description": "The date pattern to match. Also define `merge_strategy` if you want to change how this property and `condition` are merged togheter.",
4946
"type": "object",
5047
"properties": {
5148
"day_of": {
@@ -110,17 +107,115 @@
110107
}
111108
}
112109
}
110+
},
111+
"condition": {
112+
"description": "The custom predicate to match. Also define `merge_strategy` if you want to change how this property and `time` are merged togheter.",
113+
"type": "object",
114+
"properties": {
115+
"shell": {
116+
"description": "The inputs to pass to the spawned shell. This could either be a simple shell command, or a script that you pass into a shell or interpreter. A return code of 0 means `true`, any other value means `false`. Also define `merge_strategy` if you want to change how this property and `predicate` are merged togheter.\nThe following environment variables are available to you:\n - `DAY_OF_WEEK`: The name of day of the week. (ex. \"Tuesday\")\n - `DAY_IN_WEEK`: The number of days after the week has started. Week boundary is set in `week_start_day`.\n - `DAY_OF_MONTH`: The day of the month.\n - `WEEK`: The week number in the year.\n - `MONTH`: The month number.\n - `YEAR`: The year in your system's default locale's year format, usually AD.",
117+
"type": "object",
118+
"required": [
119+
"run"
120+
],
121+
"properties": {
122+
"run": {
123+
"description": "The command or script to pass into the shell/interpreter. If `shell_args` is not specified, then an appropriate flag (`-c` or `/C`) is automatically added to tell the shell to run the input.",
124+
"type": "string"
125+
},
126+
"shell": {
127+
"description": "The optional host shell/interpreter. Accepts a path to an executable or just the executable name if it is already in the PATH. Defaults to `sh` on Linux/macOS, and `cmd.exe` on Windows.",
128+
"type": "string"
129+
},
130+
"shell_args": {
131+
"description": "Optional shell arguments as an array to pass to the shell/interpreter. If this is specified, then `occasion` will not add the script flag (`-c` or `/C`) for you. Keep in mind that the command/script in `run` is added last.",
132+
"type": "array",
133+
"items": {
134+
"type": "string"
135+
}
136+
}
137+
}
138+
},
139+
"predicate": {
140+
"description": "A custom boolean expression to evaluate. Useful if you do not want to evaluate shell args. Also define `merge_strategy` if you want to change how this property and `shell` are merged togheter.\nThe following environment variables are available to you:\n - `DAY_IN_WEEK`: The number of days after the week has started. Week boundary is set in `week_start_day`.\n - `DAY_OF_MONTH`: The day of the month.\n - `WEEK`: The week number in the year.\n - `MONTH`: The month number.\n - `YEAR`: The year in your system's default locale's year format, usually AD.",
141+
"type": "string"
142+
},
143+
"merge_strategy": {
144+
"description": "If `shell` and `predicate` are both defined, This defines how both results should be combined. Accepts boolean operators `AND (and/both/&)`, `OR (or/any/|)`, `XOR (xor/either/^)`, `NAND (nand)` and `NOR (nor/neither)`.",
145+
"type": "string",
146+
"enum": [
147+
"AND",
148+
"and",
149+
"both",
150+
"&",
151+
"OR",
152+
"or",
153+
"any",
154+
"|",
155+
"XOR",
156+
"xor",
157+
"either",
158+
"^",
159+
"NAND",
160+
"nand",
161+
"NOR",
162+
"nor",
163+
"neither"
164+
]
165+
}
166+
}
167+
},
168+
"merge_strategy": {
169+
"description": "If `time` and `condition` are both defined, This defines how both results should be combined. Accepts boolean operators `AND (and/both/&)`, `OR (or/any/|)`, `XOR (xor/either/^)`, `NAND (nand)` and `NOR (nor/neither)`.",
170+
"type": "string",
171+
"enum": [
172+
"AND",
173+
"and",
174+
"both",
175+
"&",
176+
"OR",
177+
"or",
178+
"any",
179+
"|",
180+
"XOR",
181+
"xor",
182+
"either",
183+
"^",
184+
"NAND",
185+
"nand",
186+
"NOR",
187+
"nor",
188+
"neither"
189+
]
113190
}
114191
},
115192
"anyOf": [
116193
{
117-
"required": [
118-
"message"
194+
"anyOf": [
195+
{
196+
"required": [
197+
"message"
198+
]
199+
},
200+
{
201+
"required": [
202+
"command"
203+
]
204+
}
119205
]
120206
},
121207
{
122-
"required": [
123-
"command"
208+
"anyOf": [
209+
{
210+
"required": [
211+
"time"
212+
]
213+
},
214+
{
215+
"required": [
216+
"condition"
217+
]
218+
}
124219
]
125220
}
126221
]

src/config.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,40 @@ pub struct Config {
2323
pub struct TimeRangeMessage {
2424
pub message: Option<String>,
2525
pub command: Option<CustomCommand>,
26-
pub time: TimeRange,
26+
pub time: Option<TimeRange>,
27+
pub condition: Option<RunCondition>,
28+
#[serde(default)]
29+
pub merge_strategy: MergeStratagy,
30+
}
31+
32+
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
33+
pub struct RunCondition {
34+
pub shell: Option<CustomCommand>,
35+
pub predicate: Option<String>,
36+
#[serde(default)]
37+
pub merge_strategy: MergeStratagy,
38+
}
39+
40+
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
41+
pub enum MergeStratagy {
42+
#[serde(alias = "and")]
43+
#[serde(alias = "both")]
44+
#[serde(alias = "&")]
45+
AND,
46+
#[default]
47+
#[serde(alias = "or")]
48+
#[serde(alias = "any")]
49+
#[serde(alias = "|")]
50+
OR,
51+
#[serde(alias = "xor")]
52+
#[serde(alias = "either")]
53+
#[serde(alias = "^")]
54+
XOR,
55+
#[serde(alias = "nand")]
56+
NAND,
57+
#[serde(alias = "nor")]
58+
#[serde(alias = "neither")]
59+
NOR,
2760
}
2861

2962
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
@@ -187,29 +220,29 @@ mod unit_tests {
187220
let test_config = Config {
188221
dates: vec![TimeRangeMessage {
189222
message: Some("hai :3".to_string()),
190-
command: None,
191-
time: TimeRange {
223+
time: Some(TimeRange {
192224
day_of: Some(DayOf::Month(hash_set! {1,3,5,7,9})),
193225
month: Some(hash_set! {Month::January,Month::June,Month::July}),
194226
year: Some(hash_set! {2016,2017,2018,2022,2024,2005,2030}),
195-
},
227+
}),
228+
..Default::default()
196229
}],
197230
multiple_behavior: Some(MultipleBehavior::default()),
198231
week_start_day: None,
199232
};
200233
let test_config_2 = Config {
201234
dates: vec![TimeRangeMessage {
202-
message: None,
203235
command: Some(CustomCommand {
204236
run: "echo \"Hello!\"".to_string(),
205237
shell: None,
206238
shell_flags: None,
207239
}),
208-
time: TimeRange {
240+
time: Some(TimeRange {
209241
day_of: Some(DayOf::Month(hash_set! { 2 })),
210242
month: None,
211243
year: None,
212-
},
244+
}),
245+
..Default::default()
213246
}],
214247
multiple_behavior: Some(MultipleBehavior::First),
215248
week_start_day: None,
@@ -229,12 +262,12 @@ mod unit_tests {
229262
let test_config = Config {
230263
dates: vec![TimeRangeMessage {
231264
message: Some("hai :3".to_string()),
232-
command: None,
233-
time: TimeRange {
265+
time: Some(TimeRange {
234266
day_of: Some(DayOf::Month(hash_set! { 1, 3, 5, 7, 9 })),
235267
month: Some(hash_set! { Month::January, Month::June, Month::July }),
236268
year: Some(hash_set! { 2016, 2017, 2018, 2022, 2024, 2005, 2030 }),
237-
},
269+
}),
270+
..Default::default()
238271
}],
239272
multiple_behavior: Some(MultipleBehavior::default()),
240273
week_start_day: None,
@@ -254,12 +287,12 @@ mod unit_tests {
254287
let test_config = Config {
255288
dates: vec![TimeRangeMessage {
256289
message: Some("hai :3".to_string()),
257-
command: None,
258-
time: TimeRange {
290+
time: Some(TimeRange {
259291
day_of: Some(DayOf::Month(hash_set! { 1, 3, 5, 7, 9 })),
260292
month: Some(hash_set! { Month::January, Month::June, Month::July }),
261293
year: Some(hash_set! { 2016, 2017, 2018, 2022, 2024, 2005, 2030 }),
262-
},
294+
}),
295+
..Default::default()
263296
}],
264297
multiple_behavior: Some(MultipleBehavior::default()),
265298
week_start_day: None,
@@ -298,12 +331,12 @@ mod unit_tests {
298331
let test_config = Config {
299332
dates: vec![TimeRangeMessage {
300333
message: Some("hai :3".to_string()),
301-
command: None,
302-
time: TimeRange {
334+
time: Some(TimeRange {
303335
day_of: Some(DayOf::Month(hash_set! { 1, 3, 5, 7, 9 })),
304336
month: Some(hash_set! { Month::January, Month::June, Month::July }),
305337
year: Some(hash_set! { 2016, 2017, 2018, 2022, 2024, 2005, 2030 }),
306-
},
338+
}),
339+
..Default::default()
307340
}],
308341
multiple_behavior: Some(MultipleBehavior::default()),
309342
week_start_day: None,

0 commit comments

Comments
 (0)