1use std::{
2 cmp::Ordering,
3 collections::HashMap,
4 convert::Infallible,
5 error::Error,
6 fmt::{Display, Formatter},
7};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct Versioning(HashMap<PackageName, ChangeType>);
14
15impl From<(&str, ChangeType)> for Versioning {
16 fn from(value: (&str, ChangeType)) -> Self {
17 let value = (PackageName::from(value.0), value.1);
18 Self::from(value)
19 }
20}
21
22impl From<(PackageName, ChangeType)> for Versioning {
23 fn from(value: (PackageName, ChangeType)) -> Self {
24 let mut map = HashMap::new();
25 map.insert(value.0, value.1);
26 Self(map)
27 }
28}
29
30impl Versioning {
31 pub fn try_from_iter<Key, Value, ParseError, Iter>(
37 iter: Iter,
38 ) -> Result<Self, BuildVersioningError>
39 where
40 Key: Into<PackageName>,
41 Value: TryInto<ChangeType, Error = ParseError>,
42 ParseError: Into<BuildVersioningError>,
43 Iter: IntoIterator<Item = (Key, Value)>,
44 {
45 let map = iter
46 .into_iter()
47 .map(|(key, value)| {
48 value
49 .try_into()
50 .map_err(Into::into)
51 .map(|value| (key.into(), value))
52 })
53 .collect::<Result<HashMap<PackageName, ChangeType>, BuildVersioningError>>()?;
54 if map.is_empty() {
55 Err(BuildVersioningError::EmptyVersioningError)
56 } else {
57 Ok(Self(map))
58 }
59 }
60
61 pub fn iter(&self) -> impl Iterator<Item = (&PackageName, &ChangeType)> {
62 self.0.iter()
63 }
64
65 #[must_use]
66 pub fn len(&self) -> usize {
67 self.0.len()
68 }
69
70 #[must_use]
71 pub fn is_empty(&self) -> bool {
72 self.0.is_empty()
73 }
74}
75
76impl IntoIterator for Versioning {
77 type Item = (PackageName, ChangeType);
78 type IntoIter = std::collections::hash_map::IntoIter<PackageName, ChangeType>;
79
80 fn into_iter(self) -> Self::IntoIter {
81 self.0.into_iter()
82 }
83}
84
85impl FromIterator<(PackageName, ChangeType)> for Versioning {
86 fn from_iter<T: IntoIterator<Item = (PackageName, ChangeType)>>(iter: T) -> Self {
87 Self(iter.into_iter().collect())
88 }
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum BuildVersioningError {
94 EmptyVersioningError,
96}
97
98impl Display for BuildVersioningError {
99 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100 match self {
101 Self::EmptyVersioningError => {
102 f.write_str("Versioning needs to contain at least one item.")
103 }
104 }
105 }
106}
107
108impl From<Infallible> for BuildVersioningError {
109 fn from(_: Infallible) -> Self {
110 unreachable!()
111 }
112}
113
114impl Error for BuildVersioningError {}
115
116pub type PackageName = String;
118
119#[derive(Clone, Debug, Eq, PartialEq)]
122pub enum ChangeType {
123 Patch,
124 Minor,
125 Major,
126 Custom(String),
127}
128
129impl Display for ChangeType {
130 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
131 match self {
132 ChangeType::Custom(label) => write!(f, "{label}"),
133 ChangeType::Patch => write!(f, "patch"),
134 ChangeType::Minor => write!(f, "minor"),
135 ChangeType::Major => write!(f, "major"),
136 }
137 }
138}
139
140impl From<&str> for ChangeType {
141 fn from(s: &str) -> Self {
142 match s {
143 "patch" => ChangeType::Patch,
144 "minor" => ChangeType::Minor,
145 "major" => ChangeType::Major,
146 other => ChangeType::Custom(other.to_string()),
147 }
148 }
149}
150
151impl From<String> for ChangeType {
152 fn from(s: String) -> Self {
153 match s.as_str() {
154 "patch" => ChangeType::Patch,
155 "minor" => ChangeType::Minor,
156 "major" => ChangeType::Major,
157 _ => ChangeType::Custom(s),
158 }
159 }
160}
161
162impl Ord for ChangeType {
163 fn cmp(&self, other: &Self) -> Ordering {
164 match (self, other) {
165 (ChangeType::Custom(_), ChangeType::Custom(_))
166 | (ChangeType::Major, ChangeType::Major)
167 | (ChangeType::Patch, ChangeType::Patch)
168 | (ChangeType::Minor, ChangeType::Minor) => Ordering::Equal,
169 (ChangeType::Custom(_), _) => Ordering::Less,
170 (_, ChangeType::Custom(_)) => Ordering::Greater,
171 (ChangeType::Patch, _) => Ordering::Less,
172 (_, ChangeType::Patch) => Ordering::Greater,
173 (ChangeType::Minor, _) => Ordering::Less,
174 (_, ChangeType::Minor) => Ordering::Greater,
175 }
176 }
177}
178
179impl PartialOrd for ChangeType {
180 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
181 Some(self.cmp(other))
182 }
183}