1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*!
Types representing the version number of a library.
*/

use core_extensions::prelude::*;

use std::{
    error,
    fmt::{self, Display},
    num::ParseIntError,
};

use crate::std_types::StaticStr;

/// The `<major>.<minor>.<patch>` version of a library,
///
/// # Post 1.0 major version
///
/// Major versions are mutually incompatible for both users and implementors.
///
/// Minor allow users to have a version less than or equal to that of the implementor,
/// and disallows implementors from making changes that would break
/// any previous minor release (with the same major number).
///
/// Patch cannot change the api/abi of the library at all,fixes only.
///
/// # Pre 1.0 version
///
/// Minor versions are mutually incompatible for both users and implementors.
///
/// Patch cannot change the api/abi of the library at all,fixes only.
#[derive(Debug, Copy, Clone, PartialEq, Eq, StableAbi)]
#[repr(C)]
#[sabi(inside_abi_stable_crate)]
pub struct VersionStrings {
    pub major: StaticStr,
    pub minor: StaticStr,
    pub patch: StaticStr,
}

/// The parsed (`<major>.<minor>.<patch>`) version number of a library.
///
/// # Post 1.0 major version
///
/// Major versions are mutually incompatible for both users and implementors.
///
/// Minor allow users to have a version less than or equal to that of the implementor,
/// and disallows implementors from making changes that would break
/// any previous minor release (with the same major number).
///
/// Patch cannot change the api/abi of the library at all,fixes only.
#[derive(Debug, Copy, Clone, PartialEq, Eq, StableAbi)]
#[repr(C)]
#[sabi(inside_abi_stable_crate)]
pub struct VersionNumber {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
}

impl VersionStrings {
    pub fn parsed(self) -> Result<VersionNumber, ParseVersionError> {
        VersionNumber::new(self)
    }
}

impl VersionNumber {
    pub fn new(vn: VersionStrings) -> Result<Self, ParseVersionError> {
        VersionNumber {
            major: vn
                .major
                .parse()
                .map_err(|x| ParseVersionError::new(vn, "major", x))?,
            minor: vn
                .minor
                .parse()
                .map_err(|x| ParseVersionError::new(vn, "minor", x))?,
            patch: vn
                .patch
                .split_while(|x| '0' <= x && x <= '9')
                .find(|x| x.key)
                .map_or("0", |x| x.str)
                .parse()
                .map_err(|x| ParseVersionError::new(vn, "patch", x))?,
        }
        .piped(Ok)
    }

    /// Whether the `self` version number is compatible with the
    /// library_implementor version number.
    ///
    /// This uses the same semver rules as cargo:
    ///
    /// - For 0.y.z ,y is interpreted as a major version,
    ///     z is interpreted as the minor version,
    ///
    /// - For x.y.z ,x>=1,y is interpreted as a minor version.
    ///
    /// - Libraries are compatible so long as they are the same
    ///     major version with a minor_version >=`self`.
    pub fn is_compatible(self, library_implementor: VersionNumber) -> bool {
        if self.major == 0 && library_implementor.major == 0 {
            self.minor == library_implementor.minor && self.patch <= library_implementor.patch
        } else {
            self.major == library_implementor.major && self.minor <= library_implementor.minor
        }
    }
}

impl fmt::Display for VersionNumber {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
    }
}

impl fmt::Display for VersionStrings {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////


/// Instantiates a `abi_stable::version::VersionStrings` with the 
/// major.minor.patch version of the library where it is invoked.
#[macro_export]
macro_rules! package_version_strings {
    () => {{
        use $crate::std_types::StaticStr;
        $crate::version::VersionStrings {
            major: StaticStr::new(env!("CARGO_PKG_VERSION_MAJOR")),
            minor: StaticStr::new(env!("CARGO_PKG_VERSION_MINOR")),
            patch: StaticStr::new(env!("CARGO_PKG_VERSION_PATCH")),
        }
    }};
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/// When the `VersionStrings` could not be converted into a `VersionNumber`.
#[derive(Debug, Clone, PartialEq)]
pub struct ParseVersionError {
    version_strings: VersionStrings,
    which_field: &'static str,
    parse_error: ParseIntError,
}

impl ParseVersionError {
    fn new(
        version_strings: VersionStrings,
        which_field: &'static str,
        parse_error: ParseIntError,
    ) -> Self {
        Self {
            version_strings,
            which_field,
            parse_error,
        }
    }

    pub fn version_strings(&self) -> VersionStrings {
        self.version_strings
    }
}

impl Display for ParseVersionError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(
            f,
            "\nInvalid version string:'{}'\nerror at the {} field:{}",
            self.version_strings, self.which_field, self.parse_error,
        )
    }
}

impl error::Error for ParseVersionError {}