Skip to main content

tix/cli/
cli_project.rs

1#[allow(clippy::wildcard_imports)]
2use super::*;
3
4/// The `tix project` subcommands (mission `first-class-projects-01M3BYBW`, WP05).
5///
6/// A **required** subcommand, like `tix milestone` and `tix cycle`: `tix project`
7/// alone is a usage error, not a listing, because guessing which of ten verbs the
8/// user meant is worse than asking.
9///
10/// Selection is by the stable id (the registry key); a name is accepted as a
11/// convenience and resolves to the same project, since `tix.toml` guarantees the
12/// names are unique.
13#[derive(Subcommand)]
14pub enum ProjectCommand {
15    /// Create a project and allocate a stable id
16    ///
17    /// The name is a display label, not the identity: it can be changed later with
18    /// `tix project edit --name`, and every issue association survives because the
19    /// issues reference the id.
20    Create {
21        /// Display name (unique within the registry, case-insensitively)
22        #[arg(value_name = "NAME")]
23        name: String,
24        /// Long-form context
25        #[arg(long)]
26        description: Option<String>,
27        /// Free-form project status (not a board column)
28        #[arg(long)]
29        status: Option<String>,
30        /// Lead identity string
31        #[arg(long)]
32        lead: Option<String>,
33        /// Start date (`YYYY-MM-DD`)
34        #[arg(long = "start-date")]
35        start_date: Option<String>,
36        /// Due date (`YYYY-MM-DD`); must be `>= start-date`
37        #[arg(long = "due-date")]
38        due_date: Option<String>,
39    },
40    /// Edit a project's fields; a rename changes the label only
41    ///
42    /// Nothing is re-keyed: the id stays, so every association and every derived
43    /// figure is untouched. An optional text flag with a blank value clears it
44    /// (`--lead ""`), the same convention `tix edit` uses.
45    Edit {
46        /// Project id (a name is accepted as a convenience)
47        #[arg(value_name = "ID")]
48        reference: String,
49        /// New display name
50        #[arg(long)]
51        name: Option<String>,
52        /// New long-form context (`--description ""` clears)
53        #[arg(long)]
54        description: Option<String>,
55        /// New free-form status (`--status ""` clears)
56        #[arg(long)]
57        status: Option<String>,
58        /// New lead identity string (`--lead ""` clears)
59        #[arg(long)]
60        lead: Option<String>,
61        /// New start date (`YYYY-MM-DD`; `--start-date ""` clears)
62        #[arg(long = "start-date")]
63        start_date: Option<String>,
64        /// New due date (`YYYY-MM-DD`; `--due-date ""` clears)
65        #[arg(long = "due-date")]
66        due_date: Option<String>,
67    },
68    /// List projects in declaration order, with ids and member counts
69    List,
70    /// Show one project's record and its members
71    Show {
72        /// Project id (a name is accepted as a convenience)
73        #[arg(value_name = "ID")]
74        reference: String,
75    },
76    /// Delete a project's registry entry (its issues are never deleted)
77    ///
78    /// The issues that referenced the project keep their references; the report
79    /// names them so the dangling references can be repaired deliberately.
80    Delete {
81        /// Project id (a name is accepted as a convenience)
82        #[arg(value_name = "ID")]
83        reference: String,
84        /// Confirm without prompting
85        #[arg(long)]
86        yes: bool,
87    },
88    /// Associate one or more issues with a project
89    ///
90    /// Each selector is processed independently: a rejection on one is reported
91    /// and does not abort the rest.
92    Add {
93        /// Project id (a name is accepted as a convenience)
94        #[arg(value_name = "ID")]
95        reference: String,
96        /// Issue numbers, filename fragments or unique title fragments
97        #[arg(required = true, value_name = "ISSUE")]
98        issues: Vec<String>,
99    },
100    /// Dissociate one or more issues from a project
101    ///
102    /// Drops only the association; no issue is deleted, and each selector is
103    /// processed independently.
104    ///
105    /// A reference that does not name a live project is used **verbatim**, because
106    /// removing a retained stale id is the repair path a project deletion leaves
107    /// open (C-005). A typo that matches nothing is therefore an inert no-op that
108    /// reports `0 changed`, not a refusal — which is the one place this differs
109    /// from `tix edit <issue> --remove-project <id>`, where an unknown reference the
110    /// issue does not carry is refused as `unknown_project`.
111    Remove {
112        /// Project id (a name is accepted as a convenience)
113        #[arg(value_name = "ID")]
114        reference: String,
115        /// Issue numbers, filename fragments or unique title fragments
116        #[arg(required = true, value_name = "ISSUE")]
117        issues: Vec<String>,
118    },
119    /// Bulk-associate one or more issues with a project
120    ///
121    /// Project association only — general bulk editing is a different feature.
122    /// Every selector is processed independently and every outcome is reported,
123    /// so one unknown selector does not discard the associations that succeeded.
124    ///
125    /// An unknown **project** reference refuses the whole command before anything
126    /// is written (all entries then fail with `unknown_project`); use
127    /// `tix project remove` to drop a retained stale id, whose reference is matched
128    /// verbatim and reported as `0 changed` rather than refused.
129    BulkAdd {
130        /// Project id (a name is accepted as a convenience)
131        #[arg(value_name = "ID")]
132        reference: String,
133        /// Issue numbers, filename fragments or unique title fragments
134        #[arg(required = true, value_name = "ISSUE")]
135        issues: Vec<String>,
136    },
137}