tix-tracker 0.12.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
#[allow(clippy::wildcard_imports)]
use super::*;

#[derive(Subcommand)]
pub enum CycleCommand {
    Create {
        #[arg(long)]
        name: Option<String>,
        #[arg(long)]
        description: Option<String>,
        #[arg(long = "start-date")]
        start_date: Option<String>,
        #[arg(long = "due-date")]
        due_date: Option<String>,
    },
    List,
    Edit {
        #[arg(long)]
        name: String,
        #[arg(long = "new-name")]
        new_name: Option<String>,
        #[arg(long)]
        description: Option<String>,
        #[arg(long = "start-date")]
        start_date: Option<String>,
        #[arg(long = "due-date")]
        due_date: Option<String>,
    },
    Delete {
        #[arg(long)]
        name: String,
        #[arg(long)]
        yes: bool,
    },
    /// Manage repeating cycle schedules (`[cycle_schedules.<id>]`)
    ///
    /// A schedule is a stable id (the registry key) with a mutable display name;
    /// generated cycle instances are ordinary cycle records this family creates
    /// idempotently from the schedule's definition. Detection is read-only; every
    /// mutation requires `--confirm`.
    Schedule {
        #[command(subcommand)]
        command: CycleScheduleCommand,
    },
}

/// The `tix cycle schedule` subcommands (mission
/// `repeating-cycle-schedules-01M3C7W1`, WP06).
///
/// The surface contract's spelling (`contracts/cli-surface.md`): a schedule
/// namespace under `tix cycle`, with stable-ID selectors, explicit date/timezone
/// context and confirmed mutations. This enum is a thin adapter — the schedule
/// record, the read model, evaluation, generation and the lifecycle all live in
/// `crate::cycle_schedule`; nothing here re-derives a cadence, a cursor or a
/// History entry.
#[derive(Debug, Clone, Subcommand)]
pub enum CycleScheduleCommand {
    /// List schedules, one tab-separated row per schedule
    ///
    /// Columns: `id`, `name`, `state`, `cadence`, `duration`, `start_date`,
    /// `look_ahead`, `next_sequence`, `generated`.
    List,
    /// Show one schedule: identity, cadence, duration, cursor and its instances
    Show {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
    },
    /// Define a new repeating schedule; validated before persistence, requires `--confirm`
    Create(CycleScheduleCreateArgs),
    /// Change a schedule's fields, keeping its stable id; requires `--confirm`
    ///
    /// Only the flags you name change; the rest of the record (including its
    /// generation cursor and preserved metadata) is carried over.
    Edit {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleEditArgs,
        /// Confirm the change without prompting
        #[arg(long)]
        confirm: bool,
    },
    /// Delete a schedule's registry entry; requires `--confirm`
    ///
    /// The generated cycles it created and their snapshots are **not** deleted:
    /// they keep their schedule reference, which then reads as a diagnosable
    /// missing schedule, so nothing is destroyed by deleting a definition.
    Delete {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        /// Confirm the deletion without prompting
        #[arg(long)]
        confirm: bool,
    },
    /// Report ended/due/look-ahead warnings, read-only
    ///
    /// Prints the warnings and the explicit decisions each accepts. It writes
    /// nothing: no file, no cursor move, no History entry.
    Detect {
        /// Evaluate against this `YYYY-MM-DD` date instead of today
        #[arg(long, value_name = "YYYY-MM-DD")]
        date: Option<String>,
    },
    /// Create the schedule's future cycles; idempotent, requires `--confirm`
    ///
    /// A repeated call under the same stable keys is a no-op, never a duplicate.
    Generate {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        /// Generate every candidate whose start date is on or before this date
        #[arg(long, value_name = "YYYY-MM-DD")]
        through: Option<String>,
        /// Confirm the generation without prompting
        #[arg(long)]
        confirm: bool,
    },
    /// Close an instance: capture its immutable snapshot and mark it closed
    Close {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleReasonArgs,
    },
    /// Reopen a closed instance: restore `active` and append one History event
    ///
    /// The closed cycle's capacity snapshot is **not** rewritten: it stays the
    /// immutable evidence the close captured, and the appended `cycle_reopened`
    /// event names it. Reopening an open instance is a no-op, and the action
    /// requires both a reason and the instance it names.
    Reopen {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleReasonArgs,
    },
    /// Push an instance's due date out by a cadence period
    Extend {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleExtendArgs,
    },
    /// Record a sequence as skipped so it is never regenerated
    Skip {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleReasonArgs,
    },
    /// Pause a schedule and its non-terminal instances
    Pause {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleLifecycleArgs,
    },
    /// Resume a paused schedule and its instances
    Resume {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleLifecycleArgs,
    },
    /// Re-derive every future instance's dates from the schedule's definition
    AdjustFuture {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleReasonArgs,
    },
    /// Close a warning without deciding anything; nothing is written
    Ignore {
        /// Stable schedule id, or its unique display name
        #[arg(value_name = "SCHEDULE")]
        schedule: String,
        #[command(flatten)]
        args: CycleScheduleLifecycleArgs,
    },
}

/// The flags that define a schedule (`tix cycle schedule create`).
#[derive(Debug, Clone, Args)]
pub struct CycleScheduleCreateArgs {
    /// Stable schedule id (letters, digits, `-`, `_`, `:`); never reused
    #[arg(long, value_name = "ID")]
    pub id: String,
    /// Display label, unique case-insensitively within the registry
    #[arg(long, value_name = "NAME")]
    pub name: String,
    /// Cadence family: `daily`, `weekly`, `monthly` or `interval`
    ///
    /// `interval` is the family that carries `--unit`; the other three are the
    /// calendar-step families and refuse a unit.
    #[arg(long, value_name = "KIND")]
    pub cadence: String,
    /// How many cadence units between cycle starts (1-1000; absent means 1)
    #[arg(long, value_name = "N")]
    pub interval: Option<u32>,
    /// The unit an `interval` cadence counts in: `days`, `weeks`, `months`
    #[arg(long, value_name = "UNIT")]
    pub unit: Option<String>,
    /// How long one cycle lasts, as a positive count
    #[arg(long, value_name = "N")]
    pub duration: u32,
    /// The duration's unit: `days`, `weeks`, `months`
    #[arg(long = "duration-unit", value_name = "UNIT")]
    pub duration_unit: String,
    /// The schedule's stable anchor, a `YYYY-MM-DD` calendar date
    #[arg(long = "start-date", value_name = "YYYY-MM-DD")]
    pub start_date: String,
    /// Fixed UTC offset the schedule is expressed in: `UTC` or `±HH:MM`
    ///
    /// `allow_hyphen_values` so a west-of-UTC offset (`-05:00`) reaches the
    /// validator instead of being swallowed by clap as an unknown flag.
    #[arg(long, value_name = "TZ", allow_hyphen_values = true)]
    pub timezone: String,
    /// How many future cycles to consider beyond the current one (0 when absent)
    #[arg(long = "look-ahead", value_name = "N")]
    pub look_ahead: Option<u32>,
    /// Confirm the creation without prompting
    #[arg(long)]
    pub confirm: bool,
}

/// What `tix cycle schedule edit` may change. Every field is optional; only the
/// named ones change, so a partial edit keeps the rest of the record.
#[derive(Debug, Clone, Args)]
pub struct CycleScheduleEditArgs {
    /// New display label
    #[arg(long, value_name = "NAME")]
    pub name: Option<String>,
    /// New cadence family: `daily`, `weekly`, `monthly` or `interval`
    #[arg(long, value_name = "KIND")]
    pub cadence: Option<String>,
    /// New cadence interval (1-1000)
    #[arg(long, value_name = "N")]
    pub interval: Option<u32>,
    /// New cadence unit (only meaningful for the `interval` family)
    #[arg(long, value_name = "UNIT")]
    pub unit: Option<String>,
    /// New duration count
    #[arg(long, value_name = "N")]
    pub duration: Option<u32>,
    /// New duration unit
    #[arg(long = "duration-unit", value_name = "UNIT")]
    pub duration_unit: Option<String>,
    /// New anchor date, `YYYY-MM-DD`
    #[arg(long = "start-date", value_name = "YYYY-MM-DD")]
    pub start_date: Option<String>,
    /// New timezone: `UTC` or `±HH:MM` (`allow_hyphen_values` for `-05:00`)
    #[arg(long, value_name = "TZ", allow_hyphen_values = true)]
    pub timezone: Option<String>,
    /// New look-ahead count
    #[arg(long = "look-ahead", value_name = "N")]
    pub look_ahead: Option<u32>,
}

/// The flags every `tix cycle schedule` lifecycle action shares.
#[derive(Debug, Clone, Args)]
pub struct CycleScheduleLifecycleArgs {
    /// Target instance, `<schedule-id>:<sequence>` (e.g. `cs-sprint:000001`)
    #[arg(long, value_name = "CYCLE")]
    pub cycle: Option<String>,
    /// Select the instance by its start date, `YYYY-MM-DD`
    #[arg(long, value_name = "YYYY-MM-DD")]
    pub from: Option<String>,
    /// Confirm the action without prompting
    #[arg(long)]
    pub confirm: bool,
}

/// The lifecycle flags plus the reason the recording actions require.
#[derive(Debug, Clone, Args)]
pub struct CycleScheduleReasonArgs {
    #[command(flatten)]
    pub lifecycle: CycleScheduleLifecycleArgs,
    /// Why; recorded in the instance's History
    #[arg(long, value_name = "TEXT")]
    pub reason: Option<String>,
}

/// `tix cycle schedule extend`'s flags: the reason plus the period count.
#[derive(Debug, Clone, Args)]
pub struct CycleScheduleExtendArgs {
    #[command(flatten)]
    pub reason: CycleScheduleReasonArgs,
    /// How many cadence periods to extend by; the shared lifecycle supports 1
    #[arg(long, value_name = "N")]
    pub by: Option<u32>,
}