You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
4.3 KiB
204 lines
4.3 KiB
//! Version information for the file container format, semver-like internal version format.
|
|
|
|
use super::*;
|
|
use std::{
|
|
fmt,
|
|
mem::size_of,
|
|
error,
|
|
};
|
|
|
|
/// Represents other states of this container format version
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy)]
|
|
#[repr(u8)]
|
|
pub enum Tag
|
|
{
|
|
None =0,
|
|
Prerelease =1,
|
|
Unstable =2,
|
|
}
|
|
|
|
/// The error used by `Tag` and `Version` when they parse invalid input.
|
|
#[derive(Debug)]
|
|
pub struct ParsingError;
|
|
impl error::Error for ParsingError{}
|
|
impl fmt::Display for ParsingError
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "An attempt was made to parse invalid Version input")
|
|
}
|
|
}
|
|
|
|
|
|
impl Tag
|
|
{
|
|
/// Try to parse a `u8` as `Tag`.
|
|
fn try_from_u8(from: u8) -> Result<Self, ParsingError>
|
|
{
|
|
macro_rules! branches {
|
|
($($num:path),*) => {
|
|
match from {
|
|
$(
|
|
x if x == $num as u8 => $num,
|
|
)*
|
|
_ => return Err(ParsingError),
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(branches! {
|
|
Self::Prerelease,
|
|
Self::Unstable,
|
|
Self::None
|
|
})
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Tag
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
match self {
|
|
Self::Prerelease => write!(f, "*"),
|
|
Self::Unstable => write!(f, "~"),
|
|
_ => Ok(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for Tag
|
|
{
|
|
#[inline]
|
|
fn default() -> Self
|
|
{
|
|
Self::None
|
|
}
|
|
}
|
|
|
|
/// Represents a container version number in this format: `Major.Minor.Bugfix.Tag`
|
|
///
|
|
/// Should adhear to this principle
|
|
/// * Major - If different, fail parsing.
|
|
/// * Minor - If higher that current, fail parsing.
|
|
/// * Bugfix - If higher than current, warn user.
|
|
/// * Tag - Unused, but may represent things like prerelease or unstable
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy, Default)]
|
|
#[repr(packed, C)]
|
|
pub struct Version(u8,u8,u8,Tag);
|
|
|
|
impl fmt::Display for Version
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "{}.{}.{}{}", self.0, self.1, self.2, self.3)
|
|
}
|
|
}
|
|
|
|
impl Version
|
|
{
|
|
/// Is this read version compatable with a system of version `other`?
|
|
pub fn is_compat(&self, other: &Self) -> bool
|
|
{
|
|
self.0 == other.0 &&
|
|
self.1 <= other.1
|
|
}
|
|
/// Should we warn about parsing with this version when system version is `other`?
|
|
pub fn should_warn(&self, other: &Self) -> bool
|
|
{
|
|
self.2 > other.2 || self.3 == Tag::Unstable
|
|
}
|
|
|
|
/// Create a new instance of `Version`.
|
|
pub const fn new(major: u8, minor: u8, bugfix: u8, tag: Tag) -> Self
|
|
{
|
|
Self(major,minor,bugfix,tag)
|
|
}
|
|
|
|
/// Encode as u32
|
|
pub fn as_u32(&self) -> u32
|
|
{
|
|
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
|
|
unsafe{*(self as *const Self as *const u32)}
|
|
}
|
|
|
|
/// Decode u32 as self, assuming `Tag` is valid.
|
|
pub unsafe fn from_u32_unchecked(from: u32) -> Self
|
|
{
|
|
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
|
|
*(&from as *const u32 as *const Self)
|
|
}
|
|
|
|
/// Try to parse a `u32` encoded `Version`.
|
|
pub fn try_from_u32(from: u32) -> Result<Self, ParsingError>
|
|
{
|
|
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
|
|
debug_assert_eq!(size_of::<Self>(), 4);
|
|
let parts = &from as *const u32 as *const u8;
|
|
|
|
macro_rules! index {
|
|
($array:ident, $index:expr) => {
|
|
unsafe{*($array.offset($index))}
|
|
}
|
|
}
|
|
|
|
Ok(Self::new(index!(parts, 0),
|
|
index!(parts, 1),
|
|
index!(parts, 2),
|
|
Tag::try_from_u8(index!(parts, 3))?))
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u8> for Tag
|
|
{
|
|
type Error = ParsingError;
|
|
|
|
#[inline] fn try_from(from: u8) -> Result<Self, Self::Error>
|
|
{
|
|
Self::try_from_u8(from)
|
|
}
|
|
}
|
|
|
|
impl From<Tag> for u8
|
|
{
|
|
#[inline] fn from(from: Tag) -> Self
|
|
{
|
|
from as u8
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u32> for Version
|
|
{
|
|
type Error = ParsingError;
|
|
|
|
#[inline] fn try_from(from: u32) -> Result<Self, Self::Error>
|
|
{
|
|
Self::try_from_u32(from)
|
|
}
|
|
}
|
|
|
|
impl From<Version> for u32
|
|
{
|
|
#[inline] fn from(from: Version) -> Self
|
|
{
|
|
from.as_u32()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
use super::*;
|
|
#[test]
|
|
fn vers_encode()
|
|
{
|
|
let version1 = Version::new(1,0,0,Tag::Unstable);
|
|
let version2 = Version::try_from_u32(version1.as_u32()).unwrap();
|
|
let version3 = unsafe{Version::from_u32_unchecked(version1.as_u32())};
|
|
assert_eq!(format!("{}", version1), "1.0.0~");
|
|
assert_eq!(version1, version2);
|
|
assert_eq!(version2, version3);
|
|
}
|
|
}
|
|
|
|
|