use serde::{de, ser};
use std::convert::{TryFrom, TryInto};
use std::error::Error;
use std::fmt;
use std::num::{ParseIntError, TryFromIntError};
use std::ops::Deref;
use std::str::FromStr;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SafeLong(i64);
impl SafeLong {
#[inline]
pub fn min_value() -> SafeLong {
SafeLong(-(1 << 53) + 1)
}
#[inline]
pub fn max_value() -> SafeLong {
SafeLong((1 << 53) - 1)
}
#[inline]
pub fn new(value: i64) -> Result<SafeLong, BoundsError> {
if value >= *SafeLong::min_value() && value <= *SafeLong::max_value() {
Ok(SafeLong(value))
} else {
Err(BoundsError(()))
}
}
}
impl Deref for SafeLong {
type Target = i64;
#[inline]
fn deref(&self) -> &i64 {
&self.0
}
}
impl fmt::Display for SafeLong {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, fmt)
}
}
impl FromStr for SafeLong {
type Err = ParseError;
#[inline]
fn from_str(s: &str) -> Result<SafeLong, ParseError> {
let n = s
.parse()
.map_err(|e| ParseError(ParseErrorInner::Parse(e)))?;
SafeLong::new(n).map_err(|e| ParseError(ParseErrorInner::Bounds(e)))
}
}
impl ser::Serialize for SafeLong {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
s.serialize_i64(self.0)
}
}
impl<'de> de::Deserialize<'de> for SafeLong {
fn deserialize<D>(d: D) -> Result<SafeLong, D::Error>
where
D: de::Deserializer<'de>,
{
let value = i64::deserialize(d)?;
SafeLong::new(value)
.map_err(|_| de::Error::invalid_value(de::Unexpected::Signed(value), &"a safe long"))
}
}
macro_rules! impl_from {
($($t:ty),*) => {
$(
impl From<$t> for SafeLong {
#[inline]
fn from(n: $t) -> SafeLong {
SafeLong(i64::from(n))
}
}
)*
}
}
impl_from!(u8, i8, u16, i16, u32, i32);
macro_rules! impl_into {
($($t:ty),*) => {
$(
impl From<SafeLong> for $t {
#[inline]
fn from(n: SafeLong) -> $t {
n.0.into()
}
}
)*
}
}
impl_into!(i64, i128);
macro_rules! impl_try_from {
($($t:ty),*) => {
$(
impl TryFrom<$t> for SafeLong {
type Error = BoundsError;
#[inline]
fn try_from(n: $t) -> Result<SafeLong, BoundsError> {
i64::try_from(n)
.map_err(|_| BoundsError(()))
.and_then(SafeLong::new)
}
}
)*
}
}
impl_try_from!(u64, i64, u128, i128, usize, isize);
macro_rules! impl_try_into {
($($t:ty),*) => {
$(
impl TryFrom<SafeLong> for $t {
type Error = TryFromIntError;
#[inline]
fn try_from(n: SafeLong) -> Result<$t, TryFromIntError> {
n.0.try_into()
}
}
)*
};
}
impl_try_into!(u8, i8, u16, i16, u32, i32, u64, u128, usize, isize);
#[derive(Debug, Clone)]
pub struct BoundsError(());
impl fmt::Display for BoundsError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("value was out of bounds of a safe long")
}
}
impl Error for BoundsError {}
#[derive(Debug, Clone)]
enum ParseErrorInner {
Parse(ParseIntError),
Bounds(BoundsError),
}
#[derive(Debug, Clone)]
pub struct ParseError(ParseErrorInner);
impl fmt::Display for ParseError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match &self.0 {
ParseErrorInner::Parse(e) => fmt::Display::fmt(e, fmt),
ParseErrorInner::Bounds(e) => fmt::Display::fmt(e, fmt),
}
}
}
impl Error for ParseError {}