Skip to main content

mur_common/
schedule.rs

1//! Schedule — unified schedule definitions shared between mur CLI and Commander.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// How to handle missed schedule executions.
7#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
8#[serde(rename_all = "snake_case")]
9pub enum MissedPolicy {
10    /// Skip missed executions
11    #[default]
12    Skip,
13    /// Run only the latest missed execution
14    RunLatest,
15    /// Run all missed executions
16    RunAll,
17}
18
19/// Who is ticking this schedule.
20#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
21#[serde(rename_all = "snake_case")]
22pub enum ScheduleExecutor {
23    /// System cron/launchd (mur CLI solo mode)
24    #[default]
25    SystemCron,
26    /// Commander daemon tick loop
27    Commander,
28    /// Server-side tick
29    Server,
30}
31
32/// Notification target for schedule results.
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
34pub struct ScheduleNotify {
35    /// Notification type: slack_dm, slack_channel, terminal, webhook, none
36    #[serde(default, rename = "type")]
37    pub notify_type: String,
38    /// Target channel/user ID
39    #[serde(default)]
40    pub target: String,
41}
42
43/// Capability required to execute a workflow.
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45#[serde(rename_all = "snake_case")]
46pub enum Capability {
47    Shell,
48    Docker,
49    Browser,
50    Ai,
51    Http,
52    Ssh,
53}
54
55/// A schedule definition — the canonical shared type.
56///
57/// Used in `~/.mur/schedules.yaml` and synced to server.
58/// Commander adds runtime state (last_run, next_run, etc.) separately.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct Schedule {
61    /// Unique schedule ID
62    pub id: String,
63    /// Workflow name to execute
64    pub workflow: String,
65    /// Cron expression (5-7 fields)
66    pub cron: String,
67    /// Timezone (e.g. "Asia/Taipei", "UTC")
68    #[serde(default = "default_timezone")]
69    pub timezone: String,
70    /// Whether this schedule is active
71    #[serde(default = "default_enabled")]
72    pub enabled: bool,
73    /// Owner user ID (empty for solo mode)
74    #[serde(default)]
75    pub user_id: String,
76    /// Variables to pass to the workflow
77    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
78    pub variables: HashMap<String, String>,
79    /// Notification config
80    #[serde(default)]
81    pub notify: ScheduleNotify,
82    /// What to do when executions are missed
83    #[serde(default)]
84    pub on_missed: MissedPolicy,
85    /// Who is currently ticking this schedule
86    #[serde(default)]
87    pub executor: ScheduleExecutor,
88}
89
90fn default_timezone() -> String {
91    "UTC".into()
92}
93fn default_enabled() -> bool {
94    true
95}
96
97/// Container for schedules.yaml file format.
98#[derive(Debug, Serialize, Deserialize)]
99pub struct SchedulesFile {
100    #[serde(default)]
101    pub schedules: Vec<Schedule>,
102}