dropshot_api_manager_types/
versions.rs1use std::collections::BTreeMap;
7
8#[derive(Clone, Debug)]
10pub enum Versions {
11 Lockstep { version: semver::Version },
15
16 Versioned { supported_versions: SupportedVersions },
22}
23
24impl Versions {
25 pub fn new_lockstep(version: semver::Version) -> Versions {
27 Versions::Lockstep { version }
28 }
29
30 pub fn new_versioned(supported_versions: SupportedVersions) -> Versions {
32 Versions::Versioned { supported_versions }
33 }
34
35 pub fn is_versioned(&self) -> bool {
37 match self {
38 Versions::Lockstep { .. } => false,
39 Versions::Versioned { .. } => true,
40 }
41 }
42
43 pub fn is_lockstep(&self) -> bool {
45 !self.is_versioned()
46 }
47
48 pub fn iter_versions_semvers(&self) -> IterVersionsSemvers<'_> {
50 match self {
51 Versions::Lockstep { version } => IterVersionsSemvers {
52 inner: IterVersionsSemversInner::Lockstep(Some(version)),
53 },
54 Versions::Versioned { supported_versions } => IterVersionsSemvers {
55 inner: IterVersionsSemversInner::Versioned(
56 supported_versions.versions.iter(),
57 ),
58 },
59 }
60 }
61
62 pub fn iter_versioned_versions(
64 &self,
65 ) -> Option<impl Iterator<Item = &SupportedVersion> + '_> {
66 match self {
67 Versions::Lockstep { .. } => None,
68 Versions::Versioned { supported_versions } => {
69 Some(supported_versions.iter())
70 }
71 }
72 }
73}
74
75#[derive(Clone, Debug)]
76pub struct SupportedVersion {
77 semver: semver::Version,
78 label: &'static str,
79}
80
81impl SupportedVersion {
82 pub const fn new(
83 semver: semver::Version,
84 label: &'static str,
85 ) -> SupportedVersion {
86 SupportedVersion { semver, label }
87 }
88
89 pub fn semver(&self) -> &semver::Version {
90 &self.semver
91 }
92
93 pub fn label(&self) -> &str {
94 self.label
95 }
96}
97
98#[derive(Clone, Debug)]
99pub struct SupportedVersions {
100 versions: Vec<SupportedVersion>,
101}
102
103impl SupportedVersions {
104 #[track_caller]
105 pub fn new(versions: Vec<SupportedVersion>) -> SupportedVersions {
106 assert!(
107 !versions.is_empty(),
108 "at least one version of an API must be supported"
109 );
110
111 assert!(
115 versions.iter().map(|v| v.semver()).is_sorted(),
116 "list of supported versions for an API must be sorted"
117 );
118
119 let mut unique_versions = BTreeMap::new();
121 let mut unique_labels = BTreeMap::new();
122 for v in &versions {
123 if let Some(previous) =
124 unique_versions.insert(v.semver(), v.label())
125 {
126 panic!(
127 "version {} appears multiple times (labels: {:?}, {:?})",
128 v.semver(),
129 previous,
130 v.label()
131 );
132 }
133
134 if let Some(previous) = unique_labels.insert(v.label(), v.semver())
135 {
136 panic!(
137 "label {:?} appears multiple times (versions: {}, {})",
138 v.label(),
139 previous,
140 v.semver()
141 );
142 }
143 }
144
145 SupportedVersions { versions }
146 }
147
148 pub fn iter(&self) -> impl Iterator<Item = &'_ SupportedVersion> + '_ {
149 self.versions.iter()
150 }
151}
152
153#[derive(Debug)]
154pub struct IterVersionsSemvers<'a> {
155 inner: IterVersionsSemversInner<'a>,
156}
157
158impl<'a> Iterator for IterVersionsSemvers<'a> {
159 type Item = &'a semver::Version;
160
161 fn next(&mut self) -> Option<Self::Item> {
162 self.inner.next()
163 }
164}
165
166impl<'a> ExactSizeIterator for IterVersionsSemvers<'a> {
167 fn len(&self) -> usize {
168 self.inner.len()
169 }
170}
171
172impl<'a> DoubleEndedIterator for IterVersionsSemvers<'a> {
173 fn next_back(&mut self) -> Option<Self::Item> {
174 self.inner.next_back()
175 }
176}
177
178#[derive(Debug)]
179enum IterVersionsSemversInner<'a> {
180 Lockstep(Option<&'a semver::Version>),
181 Versioned(std::slice::Iter<'a, SupportedVersion>),
182}
183
184impl<'a> Iterator for IterVersionsSemversInner<'a> {
185 type Item = &'a semver::Version;
186
187 fn next(&mut self) -> Option<Self::Item> {
188 match self {
189 IterVersionsSemversInner::Lockstep(version) => version.take(),
190 IterVersionsSemversInner::Versioned(versions) => {
191 versions.next().map(|v| &v.semver)
192 }
193 }
194 }
195
196 fn size_hint(&self) -> (usize, Option<usize>) {
197 (self.len(), Some(self.len()))
198 }
199}
200
201impl<'a> ExactSizeIterator for IterVersionsSemversInner<'a> {
202 fn len(&self) -> usize {
203 match self {
204 IterVersionsSemversInner::Lockstep(version) => {
205 usize::from(version.is_some())
206 }
207 IterVersionsSemversInner::Versioned(versions) => versions.len(),
208 }
209 }
210}
211
212impl<'a> DoubleEndedIterator for IterVersionsSemversInner<'a> {
213 fn next_back(&mut self) -> Option<Self::Item> {
214 match self {
215 IterVersionsSemversInner::Lockstep(version) => version.take(),
216 IterVersionsSemversInner::Versioned(versions) => {
217 versions.next_back().map(|v| &v.semver)
218 }
219 }
220 }
221}
222
223#[macro_export]
282macro_rules! api_versions {
283 (
284 [
285 (
286 $latest_major:literal,
287 $latest_name: ident
288 )
289 $(,
290 (
291 $major:literal,
292 $name:ident
293 )
294 )*
295 $(,)?
296 ] ) => {
297 $crate::api_versions_picky!([
298 ($latest_major, 0, 0, $latest_name)
299 $(, ($major, 0, 0, $name))*
300 ]);
301 };
302}
303
304#[macro_export]
311macro_rules! api_versions_picky {
312 ( [
313 (
314 $latest_major:literal,
315 $latest_minor:literal,
316 $latest_patch:literal,
317 $latest_name: ident
318 )
319 $(,
320 (
321 $major:literal,
322 $minor:literal,
323 $patch:literal,
324 $name:ident
325 )
326 )* $(,)? ] ) => {
327 dropshot_api_manager_types::paste! {
328 pub const [<VERSION_ $latest_name>]: $crate::semver::Version =
329 $crate::semver::Version::new($latest_major, $latest_minor, $latest_patch);
330
331 $(
332 pub const [<VERSION_ $name>]: $crate::semver::Version =
333 $crate::semver::Version::new($major, $minor, $patch);
334 )*
335
336 pub fn supported_versions() -> $crate::SupportedVersions {
337 let mut literal_versions = vec![
338 $crate::SupportedVersion::new([<VERSION_ $latest_name>], stringify!($latest_name)),
339 $( $crate::SupportedVersion::new([<VERSION_ $name>], stringify!($name)) ),*
340 ];
341 literal_versions.reverse();
342 $crate::SupportedVersions::new(literal_versions)
343 }
344
345 pub const fn latest_version() -> $crate::semver::Version {
346 [<VERSION_ $latest_name>]
347 }
348 }
349 };
350}