use super::pack::{f32_to_u32_be_bytes, f64_to_u64_be_bytes};
use super::{Bytes, Versionstamp};
use std::{borrow::Cow, cmp};
#[cfg(feature = "num-bigint")]
use num_bigint::Sign;
#[cfg(feature = "num-bigint")]
use std::convert::TryFrom;
#[derive(Clone, Debug)]
pub enum Element<'a> {
Nil,
Bytes(Bytes<'a>),
String(Cow<'a, str>),
Tuple(Vec<Element<'a>>),
Int(i64),
#[cfg(feature = "num-bigint")]
BigInt(num_bigint::BigInt),
Float(f32),
Double(f64),
Bool(bool),
#[cfg(feature = "uuid")]
Uuid(uuid::Uuid),
Versionstamp(Versionstamp),
}
struct CmpElement<'a, 'b>(&'a Element<'b>);
impl<'a, 'b> PartialEq for CmpElement<'a, 'b> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == cmp::Ordering::Equal
}
}
impl<'a, 'b> Eq for CmpElement<'a, 'b> {}
impl<'a, 'b> PartialOrd for CmpElement<'a, 'b> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a, 'b> Ord for CmpElement<'a, 'b> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0
.code()
.cmp(&other.0.code())
.then_with(|| match (&self.0, &other.0) {
(Element::Bytes(a), Element::Bytes(b)) => a.cmp(b),
(Element::String(a), Element::String(b)) => a.cmp(b),
(Element::Tuple(a), Element::Tuple(b)) => {
let a_values = a.iter().map(CmpElement);
let b_values = b.iter().map(CmpElement);
a_values.cmp(b_values)
}
(Element::Int(a), Element::Int(b)) => a.cmp(b),
#[cfg(feature = "num-bigint")]
(Element::BigInt(a), Element::BigInt(b)) => a.cmp(b),
#[cfg(feature = "num-bigint")]
(Element::BigInt(a), Element::Int(b)) => match i64::try_from(a) {
Ok(a) => a.cmp(b),
Err(_) => a.sign().cmp(&Sign::NoSign),
},
#[cfg(feature = "num-bigint")]
(Element::Int(a), Element::BigInt(b)) => match i64::try_from(b) {
Ok(b) => a.cmp(&b),
Err(_) => Sign::NoSign.cmp(&b.sign()),
},
(Element::Float(a), Element::Float(b)) => {
f32_to_u32_be_bytes(*a).cmp(&f32_to_u32_be_bytes(*b))
}
(Element::Double(a), Element::Double(b)) => {
f64_to_u64_be_bytes(*a).cmp(&f64_to_u64_be_bytes(*b))
}
#[cfg(feature = "uuid")]
(Element::Uuid(a), Element::Uuid(b)) => a.cmp(b),
(Element::Versionstamp(a), Element::Versionstamp(b)) => a.cmp(b),
_ => cmp::Ordering::Equal,
})
}
}
impl<'a> PartialEq for Element<'a> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == cmp::Ordering::Equal
}
}
impl<'a> Eq for Element<'a> {}
impl<'a> PartialOrd for Element<'a> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for Element<'a> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.cmp_at_root(other)
}
}
impl<'a> Element<'a> {
fn code(&self) -> u8 {
match self {
Element::Nil => super::NIL,
Element::Bytes(_) => super::BYTES,
Element::String(_) => super::STRING,
Element::Tuple(_) => super::NESTED,
Element::Int(_) => super::INTZERO,
#[cfg(feature = "num-bigint")]
Element::BigInt(_) => super::INTZERO,
Element::Float(_) => super::FLOAT,
Element::Double(_) => super::DOUBLE,
Element::Bool(v) => {
if *v {
super::TRUE
} else {
super::FALSE
}
}
#[cfg(feature = "uuid")]
Element::Uuid(_) => super::UUID,
Element::Versionstamp(_) => super::VERSIONSTAMP,
}
}
#[inline]
fn cmp_values(&self) -> &[Self] {
match self {
Element::Tuple(v) => v.as_slice(),
v => std::slice::from_ref(v),
}
}
fn cmp_at_root<'b>(&self, b: &Element<'b>) -> cmp::Ordering {
let a_values = self.cmp_values().iter().map(CmpElement);
let b_values = b.cmp_values().iter().map(CmpElement);
a_values.cmp(b_values)
}
pub fn into_owned(self) -> Element<'static> {
match self {
Element::Nil => Element::Nil,
Element::Bytes(v) => Element::Bytes(v.into_owned().into()),
Element::String(v) => Element::String(Cow::Owned(v.into_owned())),
Element::Tuple(v) => Element::Tuple(v.into_iter().map(|e| e.into_owned()).collect()),
Element::Int(v) => Element::Int(v),
#[cfg(feature = "num-bigint")]
Element::BigInt(v) => Element::BigInt(v),
Element::Float(v) => Element::Float(v),
Element::Double(v) => Element::Double(v),
Element::Bool(v) => Element::Bool(v),
#[cfg(feature = "uuid")]
Element::Uuid(v) => Element::Uuid(v),
Element::Versionstamp(v) => Element::Versionstamp(v),
}
}
pub fn as_bytes(&self) -> Option<&Bytes> {
match self {
Element::Bytes(v) => Some(v),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Element::String(v) => Some(&v),
_ => None,
}
}
pub fn as_tuple(&self) -> Option<&[Element<'a>]> {
match self {
Element::Tuple(v) => Some(v.as_slice()),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Element::Int(v) => Some(*v),
#[cfg(feature = "num-bigint")]
Element::BigInt(v) => i64::try_from(v).ok(),
_ => None,
}
}
#[cfg(feature = "num-bigint")]
pub fn as_bigint(&self) -> Option<&num_bigint::BigInt> {
match self {
Element::BigInt(v) => Some(v),
_ => None,
}
}
pub fn as_f32(&self) -> Option<f32> {
match self {
Element::Float(v) => Some(*v),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Element::Double(v) => Some(*v),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match *self {
Element::Bool(v) => Some(v),
_ => None,
}
}
#[cfg(feature = "uuid")]
pub fn as_uuid(&self) -> Option<&uuid::Uuid> {
match self {
Element::Uuid(v) => Some(v),
_ => None,
}
}
pub fn as_versionstamp(&self) -> Option<&Versionstamp> {
match self {
Element::Versionstamp(v) => Some(v),
_ => None,
}
}
}