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