use super::Result;
use crate::{error, R32, R64};
use float_cmp::ApproxEq;
use std;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged, from = "DeserializeItem")]
pub enum Item {
Nothing,
B(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F32(R32),
F64(R64),
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum DeserializeItem {
Nothing,
B(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F64(f64),
}
impl From<DeserializeItem> for Item {
fn from(deserialized: DeserializeItem) -> Item {
use DeserializeItem::*;
match deserialized {
F64(v) => Item::F64(R64::from(v)),
Nothing => Item::Nothing,
B(v) => Item::B(v),
U8(v) => Item::U8(v),
U16(v) => Item::U16(v),
U32(v) => Item::U32(v),
U64(v) => Item::U64(v),
I8(v) => Item::I8(v),
I16(v) => Item::I16(v),
I32(v) => Item::I32(v),
I64(v) => Item::I64(v),
}
}
}
impl Default for Item {
fn default() -> Self {
Item::Nothing
}
}
impl From<bool> for Item {
fn from(v: bool) -> Self {
Item::B(v)
}
}
impl From<i8> for Item {
fn from(v: i8) -> Self {
Item::I8(v)
}
}
impl From<i16> for Item {
fn from(v: i16) -> Self {
Item::I16(v)
}
}
impl From<i32> for Item {
fn from(v: i32) -> Self {
Item::I32(v)
}
}
impl From<i64> for Item {
fn from(v: i64) -> Self {
Item::I64(v)
}
}
impl From<u8> for Item {
fn from(v: u8) -> Self {
Item::U8(v)
}
}
impl From<u16> for Item {
fn from(v: u16) -> Self {
Item::U16(v)
}
}
impl From<u32> for Item {
fn from(v: u32) -> Self {
Item::U32(v)
}
}
impl From<u64> for Item {
fn from(v: u64) -> Self {
Item::U64(v)
}
}
impl From<f64> for Item {
fn from(v: f64) -> Self {
Item::F64(R64::from(v))
}
}
impl From<R64> for Item {
fn from(v: R64) -> Self {
Item::F64(v.into())
}
}
impl From<f32> for Item {
fn from(v: f32) -> Self {
Item::F32(R32::from(v))
}
}
impl From<R32> for Item {
fn from(v: R32) -> Self {
Item::F32(v)
}
}
impl<T> From<Option<T>> for Item
where
Item: From<T>,
{
fn from(v: Option<T>) -> Self {
match v {
Some(v) => Item::from(v),
None => Item::Nothing,
}
}
}
impl Item {
pub fn is_bool(&self) -> bool {
use crate::Item::*;
match *self {
B(_) => true,
_ => false,
}
}
pub fn is_numeric(&self) -> bool {
use crate::Item::*;
match *self {
F32(_) | F64(_) | U8(_) | U16(_) | U32(_) | U64(_) | I8(_)
| I16(_) | I32(_) | I64(_) => true,
B(_) | Nothing => false,
}
}
pub fn is_float(&self) -> bool {
use crate::Item::*;
match *self {
F32(_) | F64(_) => true,
Nothing | B(_) | U8(_) | U16(_) | U32(_) | U64(_) | I8(_)
| I16(_) | I32(_) | I64(_) => false,
}
}
pub fn can_convert_to_f64(&self) -> bool {
use crate::Item::*;
match *self {
B(_) | F32(_) | F64(_) | U8(_) | U16(_) | U32(_) | U64(_)
| I8(_) | I16(_) | I32(_) | I64(_) => true,
Nothing => false,
}
}
pub fn to_u64(&self) -> Result<u64> {
use crate::Item::*;
match *self {
B(v) => Ok(if v { 1u64 } else { 0u64 }),
F32(v) if v >= 0f32 => Ok(v.into_inner() as u64),
F64(v) if v >= 0f64 => Ok(v.into_inner() as u64),
U8(v) => Ok(u64::from(v)),
U16(v) => Ok(u64::from(v)),
U32(v) => Ok(u64::from(v)),
U64(v) => Ok(v),
I8(v) if v >= 0 => Ok(v as u64),
I16(v) if v >= 0 => Ok(v as u64),
I32(v) if v >= 0 => Ok(v as u64),
I64(v) if v >= 0 => Ok(v as u64),
_ => error::ConversionError {
function: "to_u64".to_string(),
value: self.clone(),
message: "Not a positive number".to_string(),
}
.fail(),
}
}
pub fn to_usize(&self) -> Result<usize> {
use crate::Item::*;
match *self {
B(v) => Ok(if v { 1usize } else { 0usize }),
F32(v) if v >= 0f32 => Ok(v.into_inner() as usize),
F64(v) if v >= 0f64 => Ok(v.into_inner() as usize),
U8(v) => Ok(v as usize),
U16(v) => Ok(v as usize),
U32(v) => Ok(v as usize),
U64(v) => Ok(v as usize),
I8(v) if v >= 0 => Ok(v as usize),
I16(v) if v >= 0 => Ok(v as usize),
I32(v) if v >= 0 => Ok(v as usize),
I64(v) if v >= 0 => Ok(v as usize),
_ => error::ConversionError {
function: "to_u64".to_string(),
value: self.clone(),
message: "Not a positive number".to_string(),
}
.fail(),
}
}
pub fn to_bool(&self) -> Result<bool> {
use crate::Item::*;
match *self {
B(v) => Ok(v),
F32(v) => Ok(v != 0f32),
F64(v) => Ok(v != 0f64),
U8(v) => Ok(v != 0),
U16(v) => Ok(v != 0),
U32(v) => Ok(v != 0),
U64(v) => Ok(v != 0),
I8(v) => Ok(v != 0),
I16(v) => Ok(v != 0),
I32(v) => Ok(v != 0),
I64(v) => Ok(v != 0),
Nothing => Ok(false),
}
}
pub fn to_float(&self) -> Result<R64> {
use crate::Item::*;
match *self {
B(v) => Ok(R64::from(if v { 1f64 } else { 0f64 })),
F32(v) => Ok(R64::from(v.into_inner() as f64)),
F64(v) => Ok(v),
U8(v) => Ok(R64::from(v as f64)),
U16(v) => Ok(R64::from(v as f64)),
U32(v) => Ok(R64::from(v as f64)),
U64(v) => Ok(R64::from(v as f64)),
I8(v) => Ok(R64::from(v as f64)),
I16(v) => Ok(R64::from(v as f64)),
I32(v) => Ok(R64::from(v as f64)),
I64(v) => Ok(R64::from(v as f64)),
Nothing => error::ConversionError {
function: "to_float".to_string(),
value: self.clone(),
message: "Not a numeric".to_string(),
}
.fail(),
}
}
pub fn is_unsigned(&self) -> bool {
use crate::Item::*;
match *self {
B(_) | U8(_) | U16(_) | U32(_) | U64(_) => true,
Nothing | F32(_) | F64(_) | I8(_) | I16(_) | I32(_)
| I64(_) => false,
}
}
pub fn to_unsigned(&self) -> Result<u64> {
use crate::Item::*;
match *self {
B(v) => Ok(if v { 1u64 } else { 0u64 }),
U8(v) => Ok(u64::from(v)),
U16(v) => Ok(u64::from(v)),
U32(v) => Ok(u64::from(v)),
U64(v) => Ok(v as u64),
F32(v) => Ok(v.into_inner() as u64),
F64(v) => Ok(v.into_inner() as u64),
I8(v) => Ok(v as u64),
I16(v) => Ok(v as u64),
I32(v) => Ok(v as u64),
I64(v) => Ok(v as u64),
Nothing => error::ConversionError {
function: "to_unsigned".to_string(),
value: self.clone(),
message: "Not a numeric".to_string(),
}
.fail(),
}
}
pub fn is_signed(&self) -> bool {
use crate::Item::*;
match *self {
I8(_) | I16(_) | I32(_) | I64(_) => true,
Nothing | B(_) | F32(_) | F64(_) | U8(_) | U16(_) | U32(_)
| U64(_) => false,
}
}
pub fn to_signed(&self) -> Result<i64> {
use crate::Item::*;
match *self {
B(v) => Ok(if v { 1i64 } else { 0i64 }),
I8(v) => Ok(i64::from(v)),
I16(v) => Ok(i64::from(v)),
I32(v) => Ok(i64::from(v)),
I64(v) => Ok(v as i64),
F32(v) => Ok(v.into_inner() as i64),
F64(v) => Ok(v.into_inner() as i64),
U8(v) => Ok(i64::from(v)),
U16(v) => Ok(i64::from(v)),
U32(v) => Ok(i64::from(v)),
U64(v) => Ok(v as i64),
Nothing => error::ConversionError {
function: "to_signed".to_string(),
value: self.clone(),
message: "Not a numeric".to_string(),
}
.fail(),
}
}
pub fn counts_as_true(&self) -> bool {
use crate::Item::*;
match *self {
B(v) => (v),
I8(v) => (v != 0i8),
I16(v) => (v != 0i16),
I32(v) => (v != 0i32),
I64(v) => (v != 0i64),
F32(v) => (v != 0f32),
F64(v) => (v != 0f64),
U8(v) => (v != 0u8),
U16(v) => (v != 0u16),
U32(v) => (v != 0),
U64(v) => (v != 0),
Nothing => (false),
}
}
}
pub trait TwoItemsExt {
fn common_type(&self) -> Self;
}
impl TwoItemsExt for (Item, Item) {
fn common_type(&self) -> Self {
let &(ref a, ref b) = self;
if a.is_numeric() && b.is_numeric() {
if a.is_float() || b.is_float() {
(
a.to_float().unwrap().into(),
b.to_float().unwrap().into(),
)
} else if a.is_signed() || b.is_signed() {
(
a.to_signed().unwrap().into(),
b.to_signed().unwrap().into(),
)
} else if a.is_unsigned() || b.is_unsigned() {
(
a.to_unsigned().unwrap().into(),
b.to_unsigned().unwrap().into(),
)
} else {
(Item::Nothing, Item::Nothing)
}
} else {
(Item::Nothing, Item::Nothing)
}
}
}
impl PartialEq for Item {
fn eq(&self, other: &Item) -> bool {
use crate::Item::*;
match (self, other) {
(&B(true), &B(true))
| (&B(false), &B(false))
| (&Nothing, &Nothing) => true,
(&Nothing, _) | (_, &Nothing) => false,
(s, o) => match ((*s).clone(), (*o).clone()).common_type() {
(I8(a), I8(b)) => a.eq(&b),
(I16(a), I16(b)) => a.eq(&b),
(I32(a), I32(b)) => a.eq(&b),
(I64(a), I64(b)) => a.eq(&b),
(U8(a), U8(b)) => a.eq(&b),
(U16(a), U16(b)) => a.eq(&b),
(U32(a), U32(b)) => a.eq(&b),
(U64(a), U64(b)) => a.eq(&b),
(F32(a), F32(b)) => a.into_inner().approx_eq(
b.into_inner(),
float_cmp::F32Margin {
epsilon: 3.0 * std::f32::EPSILON,
ulps: 3,
},
),
(F64(a), F64(b)) => a.into_inner().approx_eq(
b.into_inner(),
float_cmp::F64Margin {
epsilon: 3.0 * std::f64::EPSILON,
ulps: 3,
},
),
_ => false,
},
}
}
}
impl Eq for Item {}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Item) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for Item {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use crate::Item::*;
match (self, other) {
(&Nothing, &Nothing) => std::cmp::Ordering::Equal,
(&Nothing, _) => std::cmp::Ordering::Less,
(_, &Nothing) => std::cmp::Ordering::Greater,
(s, o) => match ((*s).clone(), (*o).clone()).common_type() {
(I8(a), I8(b)) => a.cmp(&b),
(I16(a), I16(b)) => a.cmp(&b),
(I32(a), I32(b)) => a.cmp(&b),
(I64(a), I64(b)) => a.cmp(&b),
(U8(a), U8(b)) => a.cmp(&b),
(U16(a), U16(b)) => a.cmp(&b),
(U32(a), U32(b)) => a.cmp(&b),
(U64(a), U64(b)) => a.cmp(&b),
(F32(a), F32(b)) => a.cmp(&b),
(F64(a), F64(b)) => a.cmp(&b),
_ => std::cmp::Ordering::Equal,
},
}
}
}
#[cfg(test)]
mod tests {
use crate::{Item, R64};
#[test]
fn deserialize() {
fn des(s: &str) -> serde_json::Result<Item> {
serde_json::from_str(s)
}
assert_eq!(des("55").unwrap(), Item::U8(55));
assert_eq!(des("-455").unwrap(), Item::I16(-455));
assert_eq!(des("null").unwrap(), Item::Nothing);
assert_eq!(des("6.28").unwrap(), Item::F64(R64::from(6.28)));
assert_eq!(
des("6.2831853071795862345645430898").unwrap(),
Item::F64(R64::from(6.2831853071795862345645430898))
);
assert_eq!(des("true").unwrap(), Item::B(true));
assert_eq!(des("false").unwrap(), Item::B(false));
}
#[test]
fn eq_types() {
use crate::Item::*;
assert_eq!(I8(3i8), I8(3i8));
assert_eq!(I8(3i8), U8(3u8));
assert_eq!(U8(3u8), I8(3i8));
assert_eq!(U16(3u16), I8(3i8));
assert_eq!(U32(3u32), I8(3i8));
assert_eq!(U64(3u64), I8(3i8));
}
#[test]
fn ne_types() {
use crate::Item::*;
assert_ne!(Nothing, U8(3u8));
assert_ne!(U8(3u8), Nothing);
}
#[test]
fn comparison() {
use crate::Item::*;
assert!(I8(3i8) > I8(2i8));
assert!(Nothing < I8(3i8));
assert!(I8(3i8) > Nothing);
assert!(Item::from(3.2f64) > Nothing);
assert!(Nothing < Item::from(3.2f64));
assert!(Item::from(-3.2f64) > Nothing);
assert!(Nothing < Item::from(-3.2f64));
}
#[test]
fn ensure_approx_eq_f64() {
let z = Item::from(0f64);
assert!(z == Item::from(0.00000000000000000000000000000001f64));
assert!(z == Item::from(0.0000000000000000000000000000001f64));
assert!(z == Item::from(0.000000000000000000000000000001f64));
assert!(z == Item::from(0.00000000000000000000000000001f64));
assert!(z == Item::from(0.0000000000000000000000000001f64));
assert!(z == Item::from(0.000000000000000000000000001f64));
assert!(z == Item::from(0.00000000000000000000000001f64));
assert!(z == Item::from(0.0000000000000000000000001f64));
assert!(z == Item::from(0.000000000000000000000001f64));
assert!(z == Item::from(0.00000000000000000000001f64));
assert!(z == Item::from(0.0000000000000000000001f64));
assert!(z == Item::from(0.000000000000000000001f64));
assert!(z == Item::from(0.00000000000000000001f64));
assert!(z == Item::from(0.0000000000000000001f64));
assert!(z == Item::from(0.000000000000000001f64));
assert!(z == Item::from(0.00000000000000001f64));
assert!(z == Item::from(0.0000000000000001f64));
assert!(z == Item::from(0f64));
assert!(z != Item::from(0.000000000000001f64));
assert!(z != Item::from(0.00000000000001f64));
assert!(z != Item::from(0.0000000000001f64));
assert!(z != Item::from(0.000000000001f64));
assert!(z != Item::from(0.00000000001f64));
assert!(z != Item::from(0.0000000001f64));
assert!(z != Item::from(0.000000001f64));
assert!(z != Item::from(0.00000001f64));
assert!(z != Item::from(0.0000001f64));
assert!(z != Item::from(0.000001f64));
assert!(z != Item::from(0.00001f64));
assert!(z != Item::from(0.0001f64));
assert!(z != Item::from(0.001f64));
assert!(z != Item::from(0.01f64));
assert!(z != Item::from(0.1f64));
assert!(z != Item::from(1f64));
assert!(z != Item::from(1.1f64));
assert!(z != Item::from(999999f64));
}
#[test]
fn ensure_approx_eq_f32() {
let z = Item::from(0f32);
assert!(z == Item::from(0.00000000000000000000000000000001f32));
assert!(z == Item::from(0.0000000000000000000000000000001f32));
assert!(z == Item::from(0.000000000000000000000000000001f32));
assert!(z == Item::from(0.00000000000000000000000000001f32));
assert!(z == Item::from(0.0000000000000000000000000001f32));
assert!(z == Item::from(0.000000000000000000000000001f32));
assert!(z == Item::from(0.00000000000000000000000001f32));
assert!(z == Item::from(0.0000000000000000000000001f32));
assert!(z == Item::from(0.000000000000000000000001f32));
assert!(z == Item::from(0.00000000000000000000001f32));
assert!(z == Item::from(0.0000000000000000000001f32));
assert!(z == Item::from(0.000000000000000000001f32));
assert!(z == Item::from(0.00000000000000000001f32));
assert!(z == Item::from(0.0000000000000000001f32));
assert!(z == Item::from(0.000000000000000001f32));
assert!(z == Item::from(0.00000000000000001f32));
assert!(z == Item::from(0.0000000000000001f32));
assert!(z == Item::from(0f32));
assert!(z != Item::from(0.000000000000001f32));
assert!(z != Item::from(0.00000000000001f32));
assert!(z != Item::from(0.0000000000001f32));
assert!(z != Item::from(0.000000000001f32));
assert!(z != Item::from(0.00000000001f32));
assert!(z != Item::from(0.0000000001f32));
assert!(z != Item::from(0.000000001f32));
assert!(z != Item::from(0.00000001f32));
assert!(z != Item::from(0.0000001f32));
assert!(z != Item::from(0.000001f32));
assert!(z != Item::from(0.00001f32));
assert!(z != Item::from(0.0001f32));
assert!(z != Item::from(0.001f32));
assert!(z != Item::from(0.01f32));
assert!(z != Item::from(0.1f32));
assert!(z != Item::from(1f32));
assert!(z != Item::from(1.1f32));
assert!(z != Item::from(999999f32));
}
}