use std::{cmp, fmt, hash, str};
use std::time::SystemTime;
#[derive(Clone, Copy, Debug)]
pub struct State {
session: u16,
serial: Serial
}
impl State {
pub fn new() -> Self {
Self::new_with_serial(0.into())
}
pub fn new_with_serial(serial: Serial) -> Self {
State {
session: {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH).unwrap()
.as_secs() as u16
},
serial
}
}
pub fn from_parts(session: u16, serial: Serial) -> Self {
State { session, serial }
}
pub fn inc(&mut self) {
self.serial = self.serial.add(1)
}
pub fn session(self) -> u16 {
self.session
}
pub fn serial(self) -> Serial {
self.serial
}
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Serial(pub u32);
impl Serial {
pub fn from_be(value: u32) -> Self {
Serial(u32::from_be(value))
}
pub fn to_be(self) -> u32 {
self.0.to_be()
}
#[allow(clippy::should_implement_trait)]
pub fn add(self, other: u32) -> Self {
assert!(other <= 0x7FFF_FFFF);
Serial(self.0.wrapping_add(other))
}
}
impl Default for Serial {
fn default() -> Self {
Self::from(0)
}
}
impl From<u32> for Serial {
fn from(value: u32) -> Serial {
Serial(value)
}
}
impl From<Serial> for u32 {
fn from(serial: Serial) -> u32 {
serial.0
}
}
impl str::FromStr for Serial {
type Err = <u32 as str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
<u32 as str::FromStr>::from_str(s).map(Into::into)
}
}
impl fmt::Display for Serial {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl PartialEq for Serial {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialEq<u32> for Serial {
fn eq(&self, other: &u32) -> bool {
self.0.eq(other)
}
}
impl Eq for Serial { }
impl cmp::PartialOrd for Serial {
fn partial_cmp(&self, other: &Serial) -> Option<cmp::Ordering> {
match self.0.cmp(&other.0) {
cmp::Ordering::Equal => Some(cmp::Ordering::Equal),
cmp::Ordering::Less => {
let sub = other.0 - self.0;
match sub.cmp(&0x8000_0000) {
cmp::Ordering::Less => Some(cmp::Ordering::Less),
cmp::Ordering::Greater => Some(cmp::Ordering::Greater),
_ => None
}
},
cmp::Ordering::Greater => {
let sub = self.0 - other.0;
match sub.cmp(&0x8000_0000) {
cmp::Ordering::Less => Some(cmp::Ordering::Greater),
cmp::Ordering::Greater => Some(cmp::Ordering::Less),
_ => None
}
}
}
}
}
impl hash::Hash for Serial {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn good_addition() {
assert_eq!(Serial(0).add(4), Serial(4));
assert_eq!(Serial(0xFF00_0000).add(0x0F00_0000),
Serial(((0xFF00_0000u64 + 0x0F00_0000u64)
% 0x1_0000_0000) as u32));
}
#[test]
#[should_panic]
fn bad_addition() {
let _ = Serial(0).add(0x8000_0000);
}
#[test]
fn comparison() {
use std::cmp::Ordering::*;
assert_eq!(Serial(12), Serial(12));
assert_ne!(Serial(12), Serial(112));
assert_eq!(Serial(12).partial_cmp(&Serial(12)), Some(Equal));
assert_eq!(Serial(12).partial_cmp(&Serial(13)), Some(Less));
assert_ne!(Serial(12).partial_cmp(&Serial(3_000_000_012)), Some(Less));
assert_eq!(Serial(3_000_000_012).partial_cmp(&Serial(12)), Some(Less));
assert_ne!(Serial(13).partial_cmp(&Serial(12)), Some(Less));
assert_eq!(Serial(12).partial_cmp(&Serial(3_000_000_012)),
Some(Greater));
assert_ne!(Serial(12).partial_cmp(&Serial(13)), Some(Greater));
assert_eq!(Serial(13).partial_cmp(&Serial(12)), Some(Greater));
assert_ne!(Serial(3_000_000_012).partial_cmp(&Serial(12)),
Some(Greater));
assert_eq!(Serial(1).partial_cmp(&Serial(0x8000_0001)), None);
assert_eq!(Serial(0x8000_0001).partial_cmp(&Serial(1)), None);
}
}