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

//! 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);
}
}