ephemeris_release/unit/
status.rs1use crate::unit::Unit;
4use crate::version::TagVersion;
5
6#[derive(Debug, thiserror::Error)]
8#[error("unit {unit:?} has tag {tag:?} under its prefix, which is not a version")]
9pub(crate) struct UnparsableTag {
10 pub(crate) unit: String,
12 pub(crate) tag: String,
14}
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub(crate) enum UnitStatus<'t> {
19 Unversioned,
22 Pending,
24 Released {
26 latest: &'t str,
28 tagged: bool,
30 },
31}
32
33impl Unit {
34 pub(crate) fn status<'t>(&self, tags: &'t [String]) -> Result<UnitStatus<'t>, UnparsableTag> {
39 let Some((latest, tag)) = TagVersion::latest(tags, &self.tag_prefix) else {
40 if let Some(tag) = tags.first() {
41 return Err(UnparsableTag {
42 unit: self.name.clone(),
43 tag: tag.clone(),
44 });
45 }
46 return Ok(if self.version.is_unversioned() {
47 UnitStatus::Unversioned
48 } else {
49 UnitStatus::Pending
50 });
51 };
52 if tags.contains(&self.tag()) {
53 return Ok(UnitStatus::Released {
54 latest: tag,
55 tagged: true,
56 });
57 }
58 if TagVersion::new(self.version.clone(), self.revision) > latest {
59 Ok(UnitStatus::Pending)
60 } else {
61 Ok(UnitStatus::Released {
62 latest: tag,
63 tagged: false,
64 })
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::{UnitStatus, UnparsableTag};
72 use crate::state::ReleaseUnitState;
73 use crate::unit::Unit;
74
75 fn unit(version: &str) -> Unit {
76 let state: ReleaseUnitState = facet_json::from_str(
77 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}"#,
78 )
79 .unwrap();
80 Unit::configured("app", None, version.parse().unwrap(), &state)
81 }
82
83 #[test]
84 fn a_unit_at_0_0_0_without_tags_is_unversioned() {
85 assert_eq!(unit("0.0.0").status(&[]).unwrap(), UnitStatus::Unversioned);
86 }
87
88 #[test]
89 fn a_version_newer_than_every_tag_is_pending() {
90 assert_eq!(unit("1.0.0").status(&[]).unwrap(), UnitStatus::Pending);
91 assert_eq!(
92 unit("1.1.0").status(&["v1.0.0".into()]).unwrap(),
93 UnitStatus::Pending
94 );
95 }
96
97 #[test]
98 fn a_tag_covers_its_own_version_and_older_ones() {
99 let tags = ["v1.0.0".into(), "v0.9.0".into()];
100 assert_eq!(
101 unit("1.0.0").status(&tags).unwrap(),
102 UnitStatus::Released {
103 latest: "v1.0.0",
104 tagged: true,
105 }
106 );
107 assert_eq!(
108 unit("0.0.0").status(&tags).unwrap(),
109 UnitStatus::Released {
110 latest: "v1.0.0",
111 tagged: false,
112 }
113 );
114 }
115
116 #[test]
117 fn tags_that_name_no_version_are_an_error() {
118 assert!(matches!(
119 unit("1.0.0").status(&["vlatest".into()]),
120 Err(UnparsableTag { unit, tag }) if unit == "app" && tag == "vlatest"
121 ));
122 }
123}