Skip to main content

tix/cli/
cli_cycle_schedule.rs

1#[allow(clippy::wildcard_imports)]
2use super::*;
3
4#[derive(Subcommand)]
5pub enum CycleCommand {
6    Create {
7        #[arg(long)]
8        name: Option<String>,
9        #[arg(long)]
10        description: Option<String>,
11        #[arg(long = "start-date")]
12        start_date: Option<String>,
13        #[arg(long = "due-date")]
14        due_date: Option<String>,
15    },
16    List,
17    Edit {
18        #[arg(long)]
19        name: String,
20        #[arg(long = "new-name")]
21        new_name: Option<String>,
22        #[arg(long)]
23        description: Option<String>,
24        #[arg(long = "start-date")]
25        start_date: Option<String>,
26        #[arg(long = "due-date")]
27        due_date: Option<String>,
28    },
29    Delete {
30        #[arg(long)]
31        name: String,
32        #[arg(long)]
33        yes: bool,
34    },
35    /// Manage repeating cycle schedules (`[cycle_schedules.<id>]`)
36    ///
37    /// A schedule is a stable id (the registry key) with a mutable display name;
38    /// generated cycle instances are ordinary cycle records this family creates
39    /// idempotently from the schedule's definition. Detection is read-only; every
40    /// mutation requires `--confirm`.
41    Schedule {
42        #[command(subcommand)]
43        command: CycleScheduleCommand,
44    },
45}
46
47/// The `tix cycle schedule` subcommands (mission
48/// `repeating-cycle-schedules-01M3C7W1`, WP06).
49///
50/// The surface contract's spelling (`contracts/cli-surface.md`): a schedule
51/// namespace under `tix cycle`, with stable-ID selectors, explicit date/timezone
52/// context and confirmed mutations. This enum is a thin adapter — the schedule
53/// record, the read model, evaluation, generation and the lifecycle all live in
54/// `crate::cycle_schedule`; nothing here re-derives a cadence, a cursor or a
55/// History entry.
56#[derive(Debug, Clone, Subcommand)]
57pub enum CycleScheduleCommand {
58    /// List schedules, one tab-separated row per schedule
59    ///
60    /// Columns: `id`, `name`, `state`, `cadence`, `duration`, `start_date`,
61    /// `look_ahead`, `next_sequence`, `generated`.
62    List,
63    /// Show one schedule: identity, cadence, duration, cursor and its instances
64    Show {
65        /// Stable schedule id, or its unique display name
66        #[arg(value_name = "SCHEDULE")]
67        schedule: String,
68    },
69    /// Define a new repeating schedule; validated before persistence, requires `--confirm`
70    Create(CycleScheduleCreateArgs),
71    /// Change a schedule's fields, keeping its stable id; requires `--confirm`
72    ///
73    /// Only the flags you name change; the rest of the record (including its
74    /// generation cursor and preserved metadata) is carried over.
75    Edit {
76        /// Stable schedule id, or its unique display name
77        #[arg(value_name = "SCHEDULE")]
78        schedule: String,
79        #[command(flatten)]
80        args: CycleScheduleEditArgs,
81        /// Confirm the change without prompting
82        #[arg(long)]
83        confirm: bool,
84    },
85    /// Delete a schedule's registry entry; requires `--confirm`
86    ///
87    /// The generated cycles it created and their snapshots are **not** deleted:
88    /// they keep their schedule reference, which then reads as a diagnosable
89    /// missing schedule, so nothing is destroyed by deleting a definition.
90    Delete {
91        /// Stable schedule id, or its unique display name
92        #[arg(value_name = "SCHEDULE")]
93        schedule: String,
94        /// Confirm the deletion without prompting
95        #[arg(long)]
96        confirm: bool,
97    },
98    /// Report ended/due/look-ahead warnings, read-only
99    ///
100    /// Prints the warnings and the explicit decisions each accepts. It writes
101    /// nothing: no file, no cursor move, no History entry.
102    Detect {
103        /// Evaluate against this `YYYY-MM-DD` date instead of today
104        #[arg(long, value_name = "YYYY-MM-DD")]
105        date: Option<String>,
106    },
107    /// Create the schedule's future cycles; idempotent, requires `--confirm`
108    ///
109    /// A repeated call under the same stable keys is a no-op, never a duplicate.
110    Generate {
111        /// Stable schedule id, or its unique display name
112        #[arg(value_name = "SCHEDULE")]
113        schedule: String,
114        /// Generate every candidate whose start date is on or before this date
115        #[arg(long, value_name = "YYYY-MM-DD")]
116        through: Option<String>,
117        /// Confirm the generation without prompting
118        #[arg(long)]
119        confirm: bool,
120    },
121    /// Close an instance: capture its immutable snapshot and mark it closed
122    Close {
123        /// Stable schedule id, or its unique display name
124        #[arg(value_name = "SCHEDULE")]
125        schedule: String,
126        #[command(flatten)]
127        args: CycleScheduleReasonArgs,
128    },
129    /// Reopen a closed instance: restore `active` and append one History event
130    ///
131    /// The closed cycle's capacity snapshot is **not** rewritten: it stays the
132    /// immutable evidence the close captured, and the appended `cycle_reopened`
133    /// event names it. Reopening an open instance is a no-op, and the action
134    /// requires both a reason and the instance it names.
135    Reopen {
136        /// Stable schedule id, or its unique display name
137        #[arg(value_name = "SCHEDULE")]
138        schedule: String,
139        #[command(flatten)]
140        args: CycleScheduleReasonArgs,
141    },
142    /// Push an instance's due date out by a cadence period
143    Extend {
144        /// Stable schedule id, or its unique display name
145        #[arg(value_name = "SCHEDULE")]
146        schedule: String,
147        #[command(flatten)]
148        args: CycleScheduleExtendArgs,
149    },
150    /// Record a sequence as skipped so it is never regenerated
151    Skip {
152        /// Stable schedule id, or its unique display name
153        #[arg(value_name = "SCHEDULE")]
154        schedule: String,
155        #[command(flatten)]
156        args: CycleScheduleReasonArgs,
157    },
158    /// Pause a schedule and its non-terminal instances
159    Pause {
160        /// Stable schedule id, or its unique display name
161        #[arg(value_name = "SCHEDULE")]
162        schedule: String,
163        #[command(flatten)]
164        args: CycleScheduleLifecycleArgs,
165    },
166    /// Resume a paused schedule and its instances
167    Resume {
168        /// Stable schedule id, or its unique display name
169        #[arg(value_name = "SCHEDULE")]
170        schedule: String,
171        #[command(flatten)]
172        args: CycleScheduleLifecycleArgs,
173    },
174    /// Re-derive every future instance's dates from the schedule's definition
175    AdjustFuture {
176        /// Stable schedule id, or its unique display name
177        #[arg(value_name = "SCHEDULE")]
178        schedule: String,
179        #[command(flatten)]
180        args: CycleScheduleReasonArgs,
181    },
182    /// Close a warning without deciding anything; nothing is written
183    Ignore {
184        /// Stable schedule id, or its unique display name
185        #[arg(value_name = "SCHEDULE")]
186        schedule: String,
187        #[command(flatten)]
188        args: CycleScheduleLifecycleArgs,
189    },
190}
191
192/// The flags that define a schedule (`tix cycle schedule create`).
193#[derive(Debug, Clone, Args)]
194pub struct CycleScheduleCreateArgs {
195    /// Stable schedule id (letters, digits, `-`, `_`, `:`); never reused
196    #[arg(long, value_name = "ID")]
197    pub id: String,
198    /// Display label, unique case-insensitively within the registry
199    #[arg(long, value_name = "NAME")]
200    pub name: String,
201    /// Cadence family: `daily`, `weekly`, `monthly` or `interval`
202    ///
203    /// `interval` is the family that carries `--unit`; the other three are the
204    /// calendar-step families and refuse a unit.
205    #[arg(long, value_name = "KIND")]
206    pub cadence: String,
207    /// How many cadence units between cycle starts (1-1000; absent means 1)
208    #[arg(long, value_name = "N")]
209    pub interval: Option<u32>,
210    /// The unit an `interval` cadence counts in: `days`, `weeks`, `months`
211    #[arg(long, value_name = "UNIT")]
212    pub unit: Option<String>,
213    /// How long one cycle lasts, as a positive count
214    #[arg(long, value_name = "N")]
215    pub duration: u32,
216    /// The duration's unit: `days`, `weeks`, `months`
217    #[arg(long = "duration-unit", value_name = "UNIT")]
218    pub duration_unit: String,
219    /// The schedule's stable anchor, a `YYYY-MM-DD` calendar date
220    #[arg(long = "start-date", value_name = "YYYY-MM-DD")]
221    pub start_date: String,
222    /// Fixed UTC offset the schedule is expressed in: `UTC` or `±HH:MM`
223    ///
224    /// `allow_hyphen_values` so a west-of-UTC offset (`-05:00`) reaches the
225    /// validator instead of being swallowed by clap as an unknown flag.
226    #[arg(long, value_name = "TZ", allow_hyphen_values = true)]
227    pub timezone: String,
228    /// How many future cycles to consider beyond the current one (0 when absent)
229    #[arg(long = "look-ahead", value_name = "N")]
230    pub look_ahead: Option<u32>,
231    /// Confirm the creation without prompting
232    #[arg(long)]
233    pub confirm: bool,
234}
235
236/// What `tix cycle schedule edit` may change. Every field is optional; only the
237/// named ones change, so a partial edit keeps the rest of the record.
238#[derive(Debug, Clone, Args)]
239pub struct CycleScheduleEditArgs {
240    /// New display label
241    #[arg(long, value_name = "NAME")]
242    pub name: Option<String>,
243    /// New cadence family: `daily`, `weekly`, `monthly` or `interval`
244    #[arg(long, value_name = "KIND")]
245    pub cadence: Option<String>,
246    /// New cadence interval (1-1000)
247    #[arg(long, value_name = "N")]
248    pub interval: Option<u32>,
249    /// New cadence unit (only meaningful for the `interval` family)
250    #[arg(long, value_name = "UNIT")]
251    pub unit: Option<String>,
252    /// New duration count
253    #[arg(long, value_name = "N")]
254    pub duration: Option<u32>,
255    /// New duration unit
256    #[arg(long = "duration-unit", value_name = "UNIT")]
257    pub duration_unit: Option<String>,
258    /// New anchor date, `YYYY-MM-DD`
259    #[arg(long = "start-date", value_name = "YYYY-MM-DD")]
260    pub start_date: Option<String>,
261    /// New timezone: `UTC` or `±HH:MM` (`allow_hyphen_values` for `-05:00`)
262    #[arg(long, value_name = "TZ", allow_hyphen_values = true)]
263    pub timezone: Option<String>,
264    /// New look-ahead count
265    #[arg(long = "look-ahead", value_name = "N")]
266    pub look_ahead: Option<u32>,
267}
268
269/// The flags every `tix cycle schedule` lifecycle action shares.
270#[derive(Debug, Clone, Args)]
271pub struct CycleScheduleLifecycleArgs {
272    /// Target instance, `<schedule-id>:<sequence>` (e.g. `cs-sprint:000001`)
273    #[arg(long, value_name = "CYCLE")]
274    pub cycle: Option<String>,
275    /// Select the instance by its start date, `YYYY-MM-DD`
276    #[arg(long, value_name = "YYYY-MM-DD")]
277    pub from: Option<String>,
278    /// Confirm the action without prompting
279    #[arg(long)]
280    pub confirm: bool,
281}
282
283/// The lifecycle flags plus the reason the recording actions require.
284#[derive(Debug, Clone, Args)]
285pub struct CycleScheduleReasonArgs {
286    #[command(flatten)]
287    pub lifecycle: CycleScheduleLifecycleArgs,
288    /// Why; recorded in the instance's History
289    #[arg(long, value_name = "TEXT")]
290    pub reason: Option<String>,
291}
292
293/// `tix cycle schedule extend`'s flags: the reason plus the period count.
294#[derive(Debug, Clone, Args)]
295pub struct CycleScheduleExtendArgs {
296    #[command(flatten)]
297    pub reason: CycleScheduleReasonArgs,
298    /// How many cadence periods to extend by; the shared lifecycle supports 1
299    #[arg(long, value_name = "N")]
300    pub by: Option<u32>,
301}