release_kit/cli/profile_flags.rs
1//! The flags every verb that resolves a target configuration takes: the
2//! project profile and the Git workflow, as `rk init`, `rk adopt`,
3//! `rk stage`, and `rk profile` read them.
4//!
5//! The canonical names are the long forms below. Three older spellings
6//! stay as hidden aliases, so an existing command line keeps working:
7//! `--tech` for one `--technology`, `--style` for `--release-style`, and
8//! `--workflow` for `--checkout-mode`, whose older values `worktree` and
9//! `branches` the parser still reads.
10
11use clap::Args;
12
13/// The profile and Git workflow answers an invocation states.
14#[derive(Debug, Clone, Default, Args)]
15pub struct ProfileFlags {
16 /// A technology present in the project; repeat it for several. A
17 /// supplied list replaces the configured one whole. Defaults to
18 /// detection from the version files.
19 #[arg(long = "technology", alias = "tech", value_name = "NAME")]
20 pub technology: Vec<String>,
21
22 /// The forge hosting the project: github or gitlab. Defaults to
23 /// detection from the target's git remote; a project with no forge
24 /// resolves to none.
25 #[arg(long)]
26 pub forge: Option<String>,
27
28 /// The project path on the forge, substituted into the rendered files
29 /// and recorded as the landing parameter. Defaults to detection from
30 /// the target's git remote.
31 #[arg(long)]
32 pub repo: Option<String>,
33
34 /// The release intent: automatic, where release-kit drives the
35 /// release; external, where the project releases through a process
36 /// release-kit does not drive; or none. Defaults to the observation's
37 /// proposal: automatic with the one release-bearing technology, none
38 /// where there is none.
39 #[arg(long, value_name = "MODE")]
40 pub release_mode: Option<String>,
41
42 /// The technology that states the version and takes the bot, for an
43 /// automatic release. Required where more than one release-bearing
44 /// technology is present.
45 #[arg(long, value_name = "NAME")]
46 pub release_driver: Option<String>,
47
48 /// The release style of an automatic release: trunk (the bot's
49 /// request carries auto-merge from creation) or lines (every request
50 /// waits for a human's merge).
51 #[arg(long, alias = "style", value_name = "STYLE")]
52 pub release_style: Option<String>,
53
54 /// The one permanent branch. Defaults to the configuration, the
55 /// record, and then master.
56 #[arg(long, value_name = "BRANCH")]
57 pub trunk: Option<String>,
58
59 /// Where a topic branch opens: linked-worktree (every code-changing
60 /// branch in its own linked worktree, the main checkout commits
61 /// nothing) or main-worktree (the original working tree switches to
62 /// it). Recorded as a Git workflow parameter and rendered into the
63 /// landed blocks.
64 #[arg(long, alias = "workflow", value_name = "MODE")]
65 pub checkout_mode: Option<String>,
66
67 /// Which authority moves an implementation onto the trunk: local
68 /// (the checkout squashes and records it, through `rk integrate`) or
69 /// forge (a pull request or merge request does, behind its required
70 /// check). Recorded as a Git workflow parameter and rendered into the
71 /// landed blocks. Defaults to local.
72 #[arg(long, value_name = "MODE")]
73 pub integration: Option<String>,
74
75 /// The check the release gate believes: the context the trunk ruleset
76 /// requires under forge integration, and the context the rendered
77 /// release gate judges under local integration. GitHub only.
78 #[arg(long, value_name = "NAME")]
79 pub required_check: Option<String>,
80
81 /// The workflow whose completion wakes the release gate under local
82 /// integration, named by its `name:` key rather than its filename.
83 /// GitHub only.
84 #[arg(long, value_name = "NAME")]
85 pub required_workflow: Option<String>,
86}
87
88impl ProfileFlags {
89 /// Whether no flag answered anything.
90 #[must_use]
91 pub const fn is_empty(&self) -> bool {
92 self.technology.is_empty()
93 && self.forge.is_none()
94 && self.repo.is_none()
95 && self.release_mode.is_none()
96 && self.release_driver.is_none()
97 && self.release_style.is_none()
98 && self.trunk.is_none()
99 && self.checkout_mode.is_none()
100 && self.integration.is_none()
101 && self.required_check.is_none()
102 && self.required_workflow.is_none()
103 }
104
105 /// The parsed answers, as the resolution takes them.
106 ///
107 /// # Errors
108 ///
109 /// Returns [`crate::error::RkError::Usage`] for a value outside its
110 /// vocabulary.
111 pub fn inputs(&self) -> Result<crate::profile::Inputs<'_>, crate::error::RkError> {
112 use crate::landing::{CheckoutMode, Integration, Style};
113 use crate::profile::ReleaseMode;
114 Ok(crate::profile::Inputs {
115 technologies: &self.technology,
116 forge: self.forge.as_deref(),
117 repo: self.repo.as_deref(),
118 release_mode: self
119 .release_mode
120 .as_deref()
121 .map(ReleaseMode::parse)
122 .transpose()?,
123 release_driver: self.release_driver.as_deref(),
124 style: self
125 .release_style
126 .as_deref()
127 .map(Style::parse)
128 .transpose()?,
129 trunk: self.trunk.as_deref(),
130 checkout_mode: self
131 .checkout_mode
132 .as_deref()
133 .map(CheckoutMode::parse)
134 .transpose()?,
135 integration: self
136 .integration
137 .as_deref()
138 .map(Integration::parse)
139 .transpose()?,
140 required_check: self.required_check.as_deref(),
141 required_workflow: self.required_workflow.as_deref(),
142 ..crate::profile::Inputs::default()
143 })
144 }
145}