mod addition;
mod subtraction;
use std::num::NonZero;
use crate::position::Error;
use crate::position::Number;
use crate::position::ParseError;
use crate::position::Result;
use crate::system::Base;
const _: () = {
assert!(size_of::<Position>() == size_of::<Number>());
const fn is_copy<T: Copy>() {}
is_copy::<Position>();
};
pub type Position = crate::Position<Base>;
impl Position {
pub const fn try_new(value: Number) -> Result<Self> {
if value == 0 {
return Err(Error::IncompatibleValue {
system: Base::NAME,
value,
});
}
Ok(Self {
system: Base,
value,
})
}
}
impl super::r#trait::Position<Base> for Position {}
impl std::str::FromStr for Position {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::try_new(s.parse::<Number>().map_err(|error| ParseError::Int {
system: Base::NAME,
inner: error,
value: s.to_string(),
})?)
}
}
impl TryFrom<Number> for Position {
type Error = Error;
fn try_from(value: Number) -> Result<Self> {
Self::try_new(value)
}
}
impl From<NonZero<Number>> for Position {
fn from(value: NonZero<Number>) -> Self {
Self::try_new(value.get()).unwrap()
}
}
macro_rules! position_from_smaller_number {
($from:ty) => {
impl From<NonZero<$from>> for Position {
fn from(value: NonZero<$from>) -> Self {
Self::try_new(value.get() as Number).unwrap()
}
}
impl TryFrom<$from> for Position {
type Error = Error;
fn try_from(value: $from) -> Result<Self> {
Self::try_new(value as Number)
}
}
};
}
#[cfg(feature = "position-u64")]
position_from_smaller_number!(u32);
position_from_smaller_number!(u16);
position_from_smaller_number!(u8);
#[cfg(test)]
mod tests {
use std::num::NonZeroU8;
use std::num::NonZeroU16;
#[cfg(feature = "position-u64")]
use std::num::NonZeroU32;
use crate::Position;
use crate::position::Error;
use crate::position::Number;
use crate::position::Result;
use crate::system::Base;
#[test]
fn from_number() {
let error: Result<Position<Base>> = 0u32.try_into();
assert_eq!(
error.unwrap_err(),
Error::IncompatibleValue {
system: Base::NAME,
value: 0,
}
);
let position: Position<Base> = 1u32.try_into().unwrap();
assert_eq!(position.get(), 1);
}
#[test]
fn distance() {
let a = Position::<Base>::try_from(10u8).unwrap();
let b = Position::<Base>::try_from(10u8).unwrap();
assert_eq!(a.distance_unchecked(&b), 0);
assert_eq!(b.distance_unchecked(&a), 0);
let a = Position::<Base>::try_from(Number::MAX).unwrap();
let b = Position::<Base>::try_from(1u8).unwrap();
assert_eq!(a.distance_unchecked(&b), Number::MAX - 1);
assert_eq!(b.distance_unchecked(&a), Number::MAX - 1);
}
#[test]
fn parse() {
let err = "0".parse::<Position<Base>>().unwrap_err();
assert_eq!(
err.to_string(),
"incompatible value for system \"base coordinate system\": `0`"
);
let position = "1".parse::<Position<Base>>().unwrap();
assert_eq!(position.get(), 1);
let err = "a".parse::<Position<Base>>().unwrap_err();
assert_eq!(
err.to_string(),
"parse error: failed to parse base coordinate system position from `a`: invalid digit \
found in string"
);
}
#[test]
fn from_smaller_types() -> Result<()> {
#[cfg(feature = "position-u64")]
{
let position = Position::<Base>::try_from(1u32)?;
assert_eq!(position.get(), 1);
let position = Position::<Base>::from(NonZeroU32::new(1).unwrap());
assert_eq!(position.get(), 1);
}
let position = Position::<Base>::try_from(1u16)?;
assert_eq!(position.get(), 1);
let position = Position::<Base>::from(NonZeroU16::new(1).unwrap());
assert_eq!(position.get(), 1);
let position = Position::<Base>::try_from(1u8)?;
assert_eq!(position.get(), 1);
let position = Position::<Base>::from(NonZeroU8::new(1).unwrap());
assert_eq!(position.get(), 1);
Ok(())
}
}