ephemeris_release/
unit.rs1mod status;
4
5use std::collections::BTreeMap;
6
7use ephemeris_files::RepositoryPath;
8
9pub(crate) use self::status::{UnitStatus, UnparsableTag};
10use crate::state::{Pin, ReleaseTarget, SourceDirectories};
11use crate::version::Version;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
15pub(crate) struct Unit {
16 pub name: String,
18 pub package_name: Option<String>,
20 pub version: Version,
22 pub target: ReleaseTarget,
24 pub source: SourceDirectories,
26 pub changelog: Option<RepositoryPath>,
28 pub tag_prefix: String,
30 pub dependencies: Vec<String>,
32 pub pins: BTreeMap<String, Pin>,
34 pub revision: Option<u64>,
36}
37
38impl Unit {
39 #[must_use]
41 pub(crate) fn title(&self) -> String {
42 self.revision.map_or_else(
43 || format!("{} {}", self.name, self.version),
44 |revision| format!("{} {}-{revision}", self.name, self.version),
45 )
46 }
47
48 #[must_use]
50 pub(crate) const fn version_path(&self) -> &RepositoryPath {
51 match &self.target {
52 ReleaseTarget::Cargo {
53 manifest,
54 publish: _,
55 }
56 | ReleaseTarget::Npm {
57 manifest,
58 publish: _,
59 } => manifest,
60 ReleaseTarget::VersionFile { version_file } => version_file,
61 ReleaseTarget::Document { path, key: _ } => path,
62 }
63 }
64
65 #[must_use]
68 pub(crate) fn tag(&self) -> String {
69 self.revision.map_or_else(
70 || format!("{}{}", self.tag_prefix, self.version),
71 |revision| format!("{}{}-{revision}", self.tag_prefix, self.version),
72 )
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use crate::state::ReleaseUnitState;
79 use crate::unit::Unit;
80 use crate::version::TagVersion;
81
82 fn unit() -> Unit {
83 let state: ReleaseUnitState = facet_json::from_str(
84 r#"{"target":{"ecosystem":"version-file","version_file":"VERSION"},"source":["."],"changelog":"CHANGELOG.md","dependencies":[],"pins":{},"tag_prefix":"v","kind":"library","application_bump":"none","revision_file":null}"#,
85 )
86 .unwrap();
87 Unit::configured("app", None, "1.0.0".parse().unwrap(), &state)
88 }
89
90 #[test]
91 fn tags_read_back_as_their_version_and_revision_unless_ambiguous() {
92 let versions = [
93 "0.1.0",
94 "1.2.3",
95 "1.0.0-rc.1",
96 "1.0.0-rc-1",
97 "1.0.0-3",
98 "1.0.0-3.1",
99 "1.0.0+build.7",
100 "1.0.0+build-7",
101 "1.0.0-alpha.2+build",
102 ];
103 for version in versions {
104 for revision in [None, Some(0), Some(1), Some(42)] {
105 for prefix in ["", "v", "app-v"] {
106 let mut unit = unit();
107 unit.version = version.parse().unwrap();
108 unit.revision = revision;
109 prefix.clone_into(&mut unit.tag_prefix);
110 let tag = unit.tag();
111 let round_trips = TagVersion::parse(&tag, prefix)
112 .is_some_and(|parsed| parsed.names(&unit.version, revision));
113 let plain = !version.contains(['-', '+']);
114 let ambiguous = if revision.is_some() {
115 !plain
116 } else {
117 version == "1.0.0-3"
118 };
119 assert_eq!(round_trips, !ambiguous, "{tag}");
120 }
121 }
122 }
123 }
124}