use std::collections::BTreeMap;
#[derive(Clone, Debug)]
pub enum Versions {
Lockstep { version: semver::Version },
Versioned { supported_versions: SupportedVersions },
}
impl Versions {
pub fn new_lockstep(version: semver::Version) -> Versions {
Versions::Lockstep { version }
}
pub fn new_versioned(supported_versions: SupportedVersions) -> Versions {
Versions::Versioned { supported_versions }
}
pub fn is_versioned(&self) -> bool {
match self {
Versions::Lockstep { .. } => false,
Versions::Versioned { .. } => true,
}
}
pub fn is_lockstep(&self) -> bool {
match self {
Versions::Lockstep { .. } => true,
Versions::Versioned { .. } => false,
}
}
pub fn iter_versions_semvers(&self) -> IterVersionsSemvers<'_> {
match self {
Versions::Lockstep { version } => IterVersionsSemvers {
inner: IterVersionsSemversInner::Lockstep(Some(version)),
},
Versions::Versioned { supported_versions } => IterVersionsSemvers {
inner: IterVersionsSemversInner::Versioned(
supported_versions.versions.iter(),
),
},
}
}
pub fn iter_versioned_versions(
&self,
) -> Option<impl Iterator<Item = &SupportedVersion> + '_> {
match self {
Versions::Lockstep { .. } => None,
Versions::Versioned { supported_versions } => {
Some(supported_versions.iter())
}
}
}
}
#[derive(Clone, Debug)]
pub struct SupportedVersion {
semver: semver::Version,
label: &'static str,
}
impl SupportedVersion {
pub const fn new(
semver: semver::Version,
label: &'static str,
) -> SupportedVersion {
SupportedVersion { semver, label }
}
pub fn semver(&self) -> &semver::Version {
&self.semver
}
pub fn label(&self) -> &str {
self.label
}
}
#[derive(Clone, Debug)]
pub struct SupportedVersions {
versions: Vec<SupportedVersion>,
}
impl SupportedVersions {
#[track_caller]
pub fn new(versions: Vec<SupportedVersion>) -> SupportedVersions {
assert!(
!versions.is_empty(),
"at least one version of an API must be supported"
);
assert!(
versions.iter().map(|v| v.semver()).is_sorted(),
"list of supported versions for an API must be sorted"
);
let mut unique_versions = BTreeMap::new();
let mut unique_labels = BTreeMap::new();
for v in &versions {
if let Some(previous) =
unique_versions.insert(v.semver(), v.label())
{
panic!(
"version {} appears multiple times (labels: {:?}, {:?})",
v.semver(),
previous,
v.label()
);
}
if let Some(previous) = unique_labels.insert(v.label(), v.semver())
{
panic!(
"label {:?} appears multiple times (versions: {}, {})",
v.label(),
previous,
v.semver()
);
}
}
SupportedVersions { versions }
}
pub fn iter(&self) -> impl Iterator<Item = &'_ SupportedVersion> + '_ {
self.versions.iter()
}
}
#[derive(Debug)]
pub struct IterVersionsSemvers<'a> {
inner: IterVersionsSemversInner<'a>,
}
impl<'a> Iterator for IterVersionsSemvers<'a> {
type Item = &'a semver::Version;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
#[derive(Debug)]
enum IterVersionsSemversInner<'a> {
Lockstep(Option<&'a semver::Version>),
Versioned(std::slice::Iter<'a, SupportedVersion>),
}
impl<'a> Iterator for IterVersionsSemversInner<'a> {
type Item = &'a semver::Version;
fn next(&mut self) -> Option<Self::Item> {
match self {
IterVersionsSemversInner::Lockstep(version) => version.take(),
IterVersionsSemversInner::Versioned(versions) => {
versions.next().map(|v| &v.semver)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl<'a> ExactSizeIterator for IterVersionsSemversInner<'a> {
fn len(&self) -> usize {
match self {
IterVersionsSemversInner::Lockstep(version) => {
usize::from(version.is_some())
}
IterVersionsSemversInner::Versioned(versions) => versions.len(),
}
}
}
#[macro_export]
macro_rules! api_versions {
( [ $( (
$major:literal,
$name:ident
) ),* $(,)? ] ) => {
dropshot_api_manager_types::paste! {
$(
pub const [<VERSION_ $name>]: $crate::semver::Version =
$crate::semver::Version::new($major, 0, 0);
)*
pub fn supported_versions() -> $crate::SupportedVersions {
let mut literal_versions = vec![
$( $crate::SupportedVersion::new([<VERSION_ $name>], stringify!($name)) ),*
];
literal_versions.reverse();
$crate::SupportedVersions::new(literal_versions)
}
}
};
}
#[macro_export]
macro_rules! api_versions_picky {
( [ $( (
$major:literal,
$minor:literal,
$patch:literal,
$name:ident
) ),* $(,)? ] ) => {
dropshot_api_manager_types::paste! {
$(
pub const [<VERSION_ $name>]: semver::Version =
semver::Version::new($major, $minor, $patch);
)*
#[track_caller]
pub fn supported_versions() -> SupportedVersions {
let mut literal_versions = vec![
$( SupportedVersion::new([<VERSION_ $name>], $desc) ),*
];
literal_versions.reverse();
SupportedVersions::new(literal_versions)
}
}
};
}