tix-tracker 0.11.0

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

/// The `tix project` subcommands (mission `first-class-projects-01M3BYBW`, WP05).
///
/// A **required** subcommand, like `tix milestone` and `tix cycle`: `tix project`
/// alone is a usage error, not a listing, because guessing which of ten verbs the
/// user meant is worse than asking.
///
/// Selection is by the stable id (the registry key); a name is accepted as a
/// convenience and resolves to the same project, since `tix.toml` guarantees the
/// names are unique.
#[derive(Subcommand)]
pub enum ProjectCommand {
    /// Create a project and allocate a stable id
    ///
    /// The name is a display label, not the identity: it can be changed later with
    /// `tix project edit --name`, and every issue association survives because the
    /// issues reference the id.
    Create {
        /// Display name (unique within the registry, case-insensitively)
        #[arg(value_name = "NAME")]
        name: String,
        /// Long-form context
        #[arg(long)]
        description: Option<String>,
        /// Free-form project status (not a board column)
        #[arg(long)]
        status: Option<String>,
        /// Lead identity string
        #[arg(long)]
        lead: Option<String>,
        /// Start date (`YYYY-MM-DD`)
        #[arg(long = "start-date")]
        start_date: Option<String>,
        /// Due date (`YYYY-MM-DD`); must be `>= start-date`
        #[arg(long = "due-date")]
        due_date: Option<String>,
    },
    /// Edit a project's fields; a rename changes the label only
    ///
    /// Nothing is re-keyed: the id stays, so every association and every derived
    /// figure is untouched. An optional text flag with a blank value clears it
    /// (`--lead ""`), the same convention `tix edit` uses.
    Edit {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
        /// New display name
        #[arg(long)]
        name: Option<String>,
        /// New long-form context (`--description ""` clears)
        #[arg(long)]
        description: Option<String>,
        /// New free-form status (`--status ""` clears)
        #[arg(long)]
        status: Option<String>,
        /// New lead identity string (`--lead ""` clears)
        #[arg(long)]
        lead: Option<String>,
        /// New start date (`YYYY-MM-DD`; `--start-date ""` clears)
        #[arg(long = "start-date")]
        start_date: Option<String>,
        /// New due date (`YYYY-MM-DD`; `--due-date ""` clears)
        #[arg(long = "due-date")]
        due_date: Option<String>,
    },
    /// List projects in declaration order, with ids and member counts
    List,
    /// Show one project's record and its members
    Show {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
    },
    /// Delete a project's registry entry (its issues are never deleted)
    ///
    /// The issues that referenced the project keep their references; the report
    /// names them so the dangling references can be repaired deliberately.
    Delete {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
        /// Confirm without prompting
        #[arg(long)]
        yes: bool,
    },
    /// Associate one or more issues with a project
    ///
    /// Each selector is processed independently: a rejection on one is reported
    /// and does not abort the rest.
    Add {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
        /// Issue numbers, filename fragments or unique title fragments
        #[arg(required = true, value_name = "ISSUE")]
        issues: Vec<String>,
    },
    /// Dissociate one or more issues from a project
    ///
    /// Drops only the association; no issue is deleted, and each selector is
    /// processed independently.
    ///
    /// A reference that does not name a live project is used **verbatim**, because
    /// removing a retained stale id is the repair path a project deletion leaves
    /// open (C-005). A typo that matches nothing is therefore an inert no-op that
    /// reports `0 changed`, not a refusal — which is the one place this differs
    /// from `tix edit <issue> --remove-project <id>`, where an unknown reference the
    /// issue does not carry is refused as `unknown_project`.
    Remove {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
        /// Issue numbers, filename fragments or unique title fragments
        #[arg(required = true, value_name = "ISSUE")]
        issues: Vec<String>,
    },
    /// Bulk-associate one or more issues with a project
    ///
    /// Project association only — general bulk editing is a different feature.
    /// Every selector is processed independently and every outcome is reported,
    /// so one unknown selector does not discard the associations that succeeded.
    ///
    /// An unknown **project** reference refuses the whole command before anything
    /// is written (all entries then fail with `unknown_project`); use
    /// `tix project remove` to drop a retained stale id, whose reference is matched
    /// verbatim and reported as `0 changed` rather than refused.
    BulkAdd {
        /// Project id (a name is accepted as a convenience)
        #[arg(value_name = "ID")]
        reference: String,
        /// Issue numbers, filename fragments or unique title fragments
        #[arg(required = true, value_name = "ISSUE")]
        issues: Vec<String>,
    },
}