Skip to main content

changeset_operations/operations/release/
types.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use changeset_core::{Changeset, PackageInfo, PrereleaseSpec};
5use changeset_project::GraduationState;
6use indexmap::IndexMap;
7use semver::Version;
8
9use crate::types::{PackageReleaseConfig, PackageVersion};
10
11pub struct ReleaseInput {
12    dry_run: bool,
13    convert_inherited: bool,
14    no_commit: bool,
15    no_tags: bool,
16    keep_changesets: bool,
17    force: bool,
18    per_package_config: HashMap<String, PackageReleaseConfig>,
19    global_prerelease: Option<PrereleaseSpec>,
20    graduate_all: bool,
21}
22
23impl ReleaseInput {
24    #[must_use]
25    pub fn builder() -> ReleaseInputBuilder {
26        ReleaseInputBuilder::new()
27    }
28
29    #[must_use]
30    pub fn dry_run(&self) -> bool {
31        self.dry_run
32    }
33
34    #[must_use]
35    pub fn convert_inherited(&self) -> bool {
36        self.convert_inherited
37    }
38
39    #[must_use]
40    pub fn no_commit(&self) -> bool {
41        self.no_commit
42    }
43
44    #[must_use]
45    pub fn no_tags(&self) -> bool {
46        self.no_tags
47    }
48
49    #[must_use]
50    pub fn keep_changesets(&self) -> bool {
51        self.keep_changesets
52    }
53
54    #[must_use]
55    pub fn force(&self) -> bool {
56        self.force
57    }
58
59    #[must_use]
60    pub fn per_package_config(&self) -> &HashMap<String, PackageReleaseConfig> {
61        &self.per_package_config
62    }
63
64    #[must_use]
65    pub fn global_prerelease(&self) -> Option<&PrereleaseSpec> {
66        self.global_prerelease.as_ref()
67    }
68
69    #[must_use]
70    pub fn graduate_all(&self) -> bool {
71        self.graduate_all
72    }
73}
74
75#[derive(Default)]
76pub struct ReleaseInputBuilder {
77    dry_run: bool,
78    convert_inherited: bool,
79    no_commit: bool,
80    no_tags: bool,
81    keep_changesets: bool,
82    force: bool,
83    per_package_config: HashMap<String, PackageReleaseConfig>,
84    global_prerelease: Option<PrereleaseSpec>,
85    graduate_all: bool,
86}
87
88impl ReleaseInputBuilder {
89    #[must_use]
90    pub fn new() -> Self {
91        Self::default()
92    }
93
94    #[must_use]
95    pub fn dry_run(mut self, v: bool) -> Self {
96        self.dry_run = v;
97        self
98    }
99
100    #[must_use]
101    pub fn convert_inherited(mut self, v: bool) -> Self {
102        self.convert_inherited = v;
103        self
104    }
105
106    #[must_use]
107    pub fn no_commit(mut self, v: bool) -> Self {
108        self.no_commit = v;
109        self
110    }
111
112    #[must_use]
113    pub fn no_tags(mut self, v: bool) -> Self {
114        self.no_tags = v;
115        self
116    }
117
118    #[must_use]
119    pub fn keep_changesets(mut self, v: bool) -> Self {
120        self.keep_changesets = v;
121        self
122    }
123
124    #[must_use]
125    pub fn force(mut self, v: bool) -> Self {
126        self.force = v;
127        self
128    }
129
130    #[must_use]
131    pub fn per_package_config(mut self, v: HashMap<String, PackageReleaseConfig>) -> Self {
132        self.per_package_config = v;
133        self
134    }
135
136    #[must_use]
137    pub fn global_prerelease(mut self, v: Option<PrereleaseSpec>) -> Self {
138        self.global_prerelease = v;
139        self
140    }
141
142    #[must_use]
143    pub fn graduate_all(mut self, v: bool) -> Self {
144        self.graduate_all = v;
145        self
146    }
147
148    #[must_use]
149    pub fn build(self) -> ReleaseInput {
150        ReleaseInput {
151            dry_run: self.dry_run,
152            convert_inherited: self.convert_inherited,
153            no_commit: self.no_commit,
154            no_tags: self.no_tags,
155            keep_changesets: self.keep_changesets,
156            force: self.force,
157            per_package_config: self.per_package_config,
158            global_prerelease: self.global_prerelease,
159            graduate_all: self.graduate_all,
160        }
161    }
162}
163
164#[derive(Debug, Clone)]
165pub struct ChangelogUpdate {
166    pub path: PathBuf,
167    pub package: Option<String>,
168    pub version: Version,
169    pub created: bool,
170}
171
172#[derive(Debug, Clone)]
173pub struct CommitResult {
174    pub sha: String,
175    pub message: String,
176}
177
178#[derive(Debug, Clone)]
179pub struct TagResult {
180    pub name: String,
181    pub target_sha: String,
182}
183
184#[derive(Debug, Clone, Default)]
185pub struct GitOperationResult {
186    pub commit: Option<CommitResult>,
187    pub tags_created: Vec<TagResult>,
188    pub changesets_deleted: Vec<PathBuf>,
189}
190
191#[derive(Debug, Clone)]
192pub struct ReleaseOutput {
193    pub planned_releases: Vec<PackageVersion>,
194    pub unchanged_packages: Vec<String>,
195    pub changesets_consumed: Vec<PathBuf>,
196    pub changelog_updates: Vec<ChangelogUpdate>,
197    pub git_result: Option<GitOperationResult>,
198}
199
200#[derive(Debug)]
201pub enum ReleaseOutcome {
202    DryRun(ReleaseOutput),
203    Executed(ReleaseOutput),
204    NoChangesets,
205}
206
207pub(super) struct GitOptions {
208    pub(super) should_commit: bool,
209    pub(super) should_create_tags: bool,
210    pub(super) should_delete_changesets: bool,
211}
212
213pub(super) enum PrepareResult {
214    Ready(ReleaseContext),
215    EarlyReturn(ReleaseOutcome),
216}
217
218#[derive(Debug, Clone, Copy)]
219pub(super) struct ReleaseClassification {
220    pub(super) is_prerelease_graduation: bool,
221    pub(super) is_graduating: bool,
222    pub(super) is_prerelease_release: bool,
223}
224
225pub(super) struct ReleaseContext {
226    pub(super) project: changeset_project::CargoProject,
227    pub(super) root_config: changeset_project::RootChangesetConfig,
228    pub(super) changeset_dir: PathBuf,
229    pub(super) changeset_files: Vec<PathBuf>,
230    pub(super) prerelease_state: Option<changeset_project::PrereleaseState>,
231    pub(super) graduation_state: Option<GraduationState>,
232    pub(super) per_package_config: HashMap<String, PackageReleaseConfig>,
233    pub(super) classification: ReleaseClassification,
234    pub(super) git_options: GitOptions,
235    pub(super) inherited_packages: Vec<String>,
236}
237
238#[derive(Debug, Clone)]
239pub(crate) struct ChangelogFileState {
240    pub(crate) path: PathBuf,
241    pub(crate) original_content: Option<String>,
242    pub(crate) file_existed: bool,
243}
244
245#[derive(Debug, Clone)]
246pub(crate) struct ChangesetFileState {
247    pub(crate) path: PathBuf,
248    pub(crate) original_consumed_status: Option<String>,
249    pub(crate) backup: Option<Changeset>,
250}
251
252pub(super) struct ReleasePlan {
253    pub(super) output: ReleaseOutput,
254    pub(super) package_lookup: IndexMap<String, PackageInfo>,
255    pub(super) changelog_backups: Vec<ChangelogFileState>,
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::types::PackageReleaseConfig;
262    use changeset_core::PrereleaseSpec;
263    use std::collections::HashMap;
264
265    #[test]
266    fn builder_defaults_all_false() {
267        let input = ReleaseInput::builder().build();
268
269        assert!(!input.dry_run());
270        assert!(!input.convert_inherited());
271        assert!(!input.no_commit());
272        assert!(!input.no_tags());
273        assert!(!input.keep_changesets());
274        assert!(!input.force());
275        assert!(!input.graduate_all());
276        assert!(input.per_package_config().is_empty());
277        assert!(input.global_prerelease().is_none());
278    }
279
280    #[test]
281    fn builder_sets_dry_run() {
282        let input = ReleaseInput::builder().dry_run(true).build();
283
284        assert!(input.dry_run());
285    }
286
287    #[test]
288    fn builder_sets_global_prerelease() {
289        let input = ReleaseInput::builder()
290            .global_prerelease(Some(PrereleaseSpec::Alpha))
291            .build();
292
293        let prerelease = input.global_prerelease();
294        assert!(prerelease.is_some());
295        assert_eq!(
296            prerelease.expect("should have prerelease").identifier(),
297            "alpha"
298        );
299    }
300
301    #[test]
302    fn builder_sets_per_package_config() {
303        let mut map = HashMap::new();
304        map.insert(
305            "crate-a".to_string(),
306            PackageReleaseConfig {
307                prerelease: None,
308                graduate_zero: false,
309            },
310        );
311
312        let input = ReleaseInput::builder().per_package_config(map).build();
313
314        assert!(input.per_package_config().contains_key("crate-a"));
315    }
316}