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}