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