434 lines
19 KiB
Rust
434 lines
19 KiB
Rust
use std::borrow::Cow;
|
|
use std::cmp::Ordering;
|
|
use std::collections::BTreeMap;
|
|
use std::collections::BTreeSet;
|
|
use std::hash::Hash;
|
|
use std::hash::Hasher;
|
|
use std::io;
|
|
use std::ops::Deref;
|
|
use std::str::FromStr;
|
|
use std::sync::Arc;
|
|
|
|
use crate::AtomClass;
|
|
use crate::Bytes;
|
|
use crate::CompoundClass;
|
|
use crate::DefaultDomainCodec;
|
|
use crate::Domain;
|
|
use crate::DomainEncode;
|
|
use crate::Embedded;
|
|
use crate::ExpectedKind;
|
|
use crate::IOValue;
|
|
use crate::Map;
|
|
use crate::Record;
|
|
use crate::Set;
|
|
use crate::SignedInteger;
|
|
use crate::Symbol;
|
|
use crate::TreeReader;
|
|
use crate::ValueClass;
|
|
use crate::ValueReader;
|
|
use crate::Writer;
|
|
use crate::signed_integer::OutOfRange;
|
|
use crate::write_value;
|
|
|
|
use super::float::{eq_f32, eq_f64, cmp_f32, cmp_f64};
|
|
|
|
#[derive(Clone)]
|
|
pub struct Value<D>(Arc<dyn ValueImpl<D>>);
|
|
|
|
pub trait ValueImpl<D: Domain> {
|
|
fn write_domain(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode<D>) -> io::Result<()> {
|
|
write_value(w, self, enc)
|
|
}
|
|
|
|
fn write(&self, w: &mut dyn Writer) -> io::Result<()> {
|
|
self.write_domain(w, &mut DefaultDomainCodec)
|
|
}
|
|
|
|
fn owned_value(&self) -> Value<D>;
|
|
fn value_class(&self) -> ValueClass;
|
|
|
|
fn as_boolean(&self) -> Option<bool> { None }
|
|
fn as_float(&self) -> Option<f32> { None }
|
|
fn as_double(&self) -> Option<f64> { None }
|
|
fn as_signed_integer(&self) -> Option<Cow<'_, SignedInteger>> { None }
|
|
fn as_string(&self) -> Option<Cow<'_, str>> { None }
|
|
fn as_bytestring(&self) -> Option<Cow<'_, [u8]>> { None }
|
|
fn as_symbol(&self) -> Option<Cow<'_, str>> { None }
|
|
|
|
fn as_i8(&self) -> Option<Result<i8, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u8(&self) -> Option<Result<u8, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i16(&self) -> Option<Result<i16, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u16(&self) -> Option<Result<u16, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i32(&self) -> Option<Result<i32, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u32(&self) -> Option<Result<u32, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i64(&self) -> Option<Result<i64, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u64(&self) -> Option<Result<u64, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i128(&self) -> Option<Result<i128, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u128(&self) -> Option<Result<u128, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_isize(&self) -> Option<Result<isize, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_usize(&self) -> Option<Result<usize, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
|
|
fn is_record(&self) -> bool { false }
|
|
fn label(&self) -> Value<D> { panic!("Not a record") }
|
|
|
|
fn is_sequence(&self) -> bool { false }
|
|
fn len(&self) -> usize { panic!("Not a compound value") }
|
|
fn index(&self, _i: usize) -> Value<D> { panic!("Not indexable") }
|
|
fn iter(&self) -> Box<dyn Iterator<Item = Value<D>> + '_> { panic!("Not iterable") }
|
|
|
|
fn is_set(&self) -> bool { false }
|
|
fn has(&self, _v: &Value<D>) -> bool { panic!("Not set-like") }
|
|
|
|
fn is_dictionary(&self) -> bool { false }
|
|
fn get(&self, _k: &Value<D>) -> Option<Value<D>> { panic!("Not a dictionary") }
|
|
fn entries(&self) -> Box<dyn Iterator<Item = (Value<D>, Value<D>)> + '_> { panic!("Not a dictionary") }
|
|
|
|
fn as_embedded(&self) -> Option<Cow<'_, D>> { None }
|
|
|
|
// INVARIANT: return Some() *only* when the contained collection is nonempty
|
|
fn annotations(&self) -> Option<Cow<'_, [IOValue]>> { None }
|
|
|
|
fn peeled(self: Arc<Self>) -> Value<D>;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
fn to_boolean(&self) -> Result<bool, ExpectedKind> { self.as_boolean().ok_or(ExpectedKind::Boolean) }
|
|
fn to_float(&self) -> Result<f32, ExpectedKind> { self.as_float().ok_or(ExpectedKind::Float) }
|
|
fn to_double(&self) -> Result<f64, ExpectedKind> { self.as_double().ok_or(ExpectedKind::Double) }
|
|
fn to_signed_integer(&self) -> Result<Cow<'_, SignedInteger>, ExpectedKind> { self.as_signed_integer().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_str/*ing*/(&self) -> Result<Cow<'_, str>, ExpectedKind> { self.as_string().ok_or(ExpectedKind::String) }
|
|
fn to_bytestring(&self) -> Result<Cow<'_, [u8]>, ExpectedKind> { self.as_bytestring().ok_or(ExpectedKind::ByteString) }
|
|
fn to_symbol(&self) -> Result<Cow<'_, str>, ExpectedKind> { self.as_symbol().ok_or(ExpectedKind::Symbol) }
|
|
|
|
fn to_i8(&self) -> Result<Result<i8, OutOfRange>, ExpectedKind> { self.as_i8().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_u8(&self) -> Result<Result<u8, OutOfRange>, ExpectedKind> { self.as_u8().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_i16(&self) -> Result<Result<i16, OutOfRange>, ExpectedKind> { self.as_i16().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_u16(&self) -> Result<Result<u16, OutOfRange>, ExpectedKind> { self.as_u16().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_i32(&self) -> Result<Result<i32, OutOfRange>, ExpectedKind> { self.as_i32().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_u32(&self) -> Result<Result<u32, OutOfRange>, ExpectedKind> { self.as_u32().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_i64(&self) -> Result<Result<i64, OutOfRange>, ExpectedKind> { self.as_i64().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_u64(&self) -> Result<Result<u64, OutOfRange>, ExpectedKind> { self.as_u64().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_i128(&self) -> Result<Result<i128, OutOfRange>, ExpectedKind> { self.as_i128().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_u128(&self) -> Result<Result<u128, OutOfRange>, ExpectedKind> { self.as_u128().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_isize(&self) -> Result<Result<isize, OutOfRange>, ExpectedKind> { self.as_isize().ok_or(ExpectedKind::SignedInteger) }
|
|
fn to_usize(&self) -> Result<Result<usize, OutOfRange>, ExpectedKind> { self.as_usize().ok_or(ExpectedKind::SignedInteger) }
|
|
|
|
fn is_simple_record(&self, name: &str, arity: Option<usize>) -> bool {
|
|
if !self.is_record() { return false; }
|
|
match self.label().as_symbol() {
|
|
None => false,
|
|
Some(s) => s.as_ref() == name && (arity.is_none() || arity.unwrap() == self.len())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<D: Domain> Deref for Value<D> {
|
|
type Target = dyn ValueImpl<D>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.0.deref()
|
|
}
|
|
}
|
|
|
|
impl<Err: Into<io::Error>, D: Domain + FromStr<Err = Err>> FromStr for Value<D> {
|
|
type Err = io::Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
TreeReader::from_str(s, true)
|
|
}
|
|
}
|
|
|
|
impl<D: Domain> Value<D> {
|
|
pub fn new<V: ValueImpl<D> + 'static>(v: V) -> Self {
|
|
Value(Arc::new(v))
|
|
}
|
|
|
|
pub fn from_arc(a: Arc<dyn ValueImpl<D>>) -> Self {
|
|
Value(a)
|
|
}
|
|
|
|
pub fn into_arc(self) -> Arc<dyn ValueImpl<D>> {
|
|
self.0
|
|
}
|
|
|
|
pub fn as_arc(&self) -> &Arc<dyn ValueImpl<D>> {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<D: Domain> ValueImpl<D> for Value<D> {
|
|
fn write_domain(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode<D>) -> io::Result<()> { self.deref().write_domain(w, enc) }
|
|
fn owned_value(&self) -> Value<D> { self.clone() }
|
|
fn value_class(&self) -> ValueClass { self.deref().value_class() }
|
|
fn as_boolean(&self) -> Option<bool> { self.deref().as_boolean() }
|
|
fn as_float(&self) -> Option<f32> { self.deref().as_float() }
|
|
fn as_double(&self) -> Option<f64> { self.deref().as_double() }
|
|
fn as_signed_integer(&self) -> Option<Cow<'_, SignedInteger>> { self.deref().as_signed_integer() }
|
|
fn as_string(&self) -> Option<Cow<'_, str>> { self.deref().as_string() }
|
|
fn as_bytestring(&self) -> Option<Cow<'_, [u8]>> { self.deref().as_bytestring() }
|
|
fn as_symbol(&self) -> Option<Cow<'_, str>> { self.deref().as_symbol() }
|
|
fn as_i8(&self) -> Option<Result<i8, OutOfRange>> { self.deref().as_i8() }
|
|
fn as_u8(&self) -> Option<Result<u8, OutOfRange>> { self.deref().as_u8() }
|
|
fn as_i16(&self) -> Option<Result<i16, OutOfRange>> { self.deref().as_i16() }
|
|
fn as_u16(&self) -> Option<Result<u16, OutOfRange>> { self.deref().as_u16() }
|
|
fn as_i32(&self) -> Option<Result<i32, OutOfRange>> { self.deref().as_i32() }
|
|
fn as_u32(&self) -> Option<Result<u32, OutOfRange>> { self.deref().as_u32() }
|
|
fn as_i64(&self) -> Option<Result<i64, OutOfRange>> { self.deref().as_i64() }
|
|
fn as_u64(&self) -> Option<Result<u64, OutOfRange>> { self.deref().as_u64() }
|
|
fn as_i128(&self) -> Option<Result<i128, OutOfRange>> { self.deref().as_i128() }
|
|
fn as_u128(&self) -> Option<Result<u128, OutOfRange>> { self.deref().as_u128() }
|
|
fn as_isize(&self) -> Option<Result<isize, OutOfRange>> { self.deref().as_isize() }
|
|
fn as_usize(&self) -> Option<Result<usize, OutOfRange>> { self.deref().as_usize() }
|
|
fn is_record(&self) -> bool { self.deref().is_record() }
|
|
fn label(&self) -> Value<D> { self.deref().label() }
|
|
fn is_sequence(&self) -> bool { self.deref().is_sequence() }
|
|
fn len(&self) -> usize { self.deref().len() }
|
|
fn index(&self, i: usize) -> Value<D> { self.deref().index(i) }
|
|
fn iter(&self) -> Box<dyn Iterator<Item = Value<D>> + '_> { self.deref().iter() }
|
|
fn is_set(&self) -> bool { self.deref().is_set() }
|
|
fn has(&self, v: &Value<D>) -> bool { self.deref().has(v) }
|
|
fn is_dictionary(&self) -> bool { self.deref().is_dictionary() }
|
|
fn get(&self, k: &Value<D>) -> Option<Value<D>> { self.deref().get(k) }
|
|
fn entries(&self) -> Box<dyn Iterator<Item = (Value<D>, Value<D>)> + '_> { self.deref().entries() }
|
|
fn as_embedded(&self) -> Option<Cow<'_, D>> { self.deref().as_embedded() }
|
|
fn annotations(&self) -> Option<Cow<'_, [IOValue]>> { self.deref().annotations() }
|
|
fn peeled(self: Arc<Self>) -> Value<D> { Arc::clone(&self.0).peeled() }
|
|
}
|
|
|
|
pub fn value_hash<D: Domain, V: ValueImpl<D> + ?Sized, H: Hasher>(v: &V, state: &mut H) {
|
|
match v.value_class() {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean => v.as_boolean().unwrap().hash(state),
|
|
AtomClass::Float => v.as_float().unwrap().to_bits().hash(state),
|
|
AtomClass::Double => v.as_double().unwrap().to_bits().hash(state),
|
|
AtomClass::SignedInteger => v.as_signed_integer().unwrap().hash(state),
|
|
AtomClass::String => v.as_string().unwrap().hash(state),
|
|
AtomClass::ByteString => v.as_bytestring().unwrap().hash(state),
|
|
AtomClass::Symbol => v.as_symbol().unwrap().hash(state),
|
|
}
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Sequence |
|
|
CompoundClass::Set => {
|
|
state.write_usize(v.len());
|
|
for v in v.iter() { v.hash(state) }
|
|
}
|
|
CompoundClass::Record => {
|
|
v.label().hash(state);
|
|
state.write_usize(v.len());
|
|
for v in v.iter() { v.hash(state) }
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
state.write_usize(v.len());
|
|
for (k, v) in v.entries() {
|
|
k.hash(state);
|
|
v.hash(state);
|
|
}
|
|
}
|
|
}
|
|
ValueClass::Embedded => v.as_embedded().unwrap().hash(state),
|
|
}
|
|
}
|
|
|
|
// TODO: when unstable feature iter_order_by stabilises, use that instead
|
|
fn iters_eq<D: Domain>(
|
|
i: &mut dyn Iterator<Item = Value<D>>,
|
|
j: &mut dyn Iterator<Item = Value<D>>,
|
|
) -> bool {
|
|
loop {
|
|
match i.next() {
|
|
None => return j.next().is_none(),
|
|
Some(ii) => match j.next() {
|
|
None => return false,
|
|
Some(jj) => if !value_eq(&*ii, &*jj) { return false },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn value_eq<D: Domain, V: ValueImpl<D> + ?Sized, W: ValueImpl<D> + ?Sized>(v: &V, w: &W) -> bool {
|
|
let cls = v.value_class();
|
|
if cls != w.value_class() { return false; }
|
|
match cls {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean =>
|
|
v.as_boolean().unwrap() == w.as_boolean().unwrap(),
|
|
AtomClass::Float =>
|
|
eq_f32(v.as_float().unwrap(), w.as_float().unwrap()),
|
|
AtomClass::Double =>
|
|
eq_f64(v.as_double().unwrap(), w.as_double().unwrap()),
|
|
AtomClass::SignedInteger =>
|
|
v.as_signed_integer().unwrap() == w.as_signed_integer().unwrap(),
|
|
AtomClass::String =>
|
|
v.as_string().unwrap() == w.as_string().unwrap(),
|
|
AtomClass::ByteString =>
|
|
v.as_bytestring().unwrap() == w.as_bytestring().unwrap(),
|
|
AtomClass::Symbol =>
|
|
v.as_symbol().unwrap() == w.as_symbol().unwrap(),
|
|
}
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Record => {
|
|
if !value_eq(&*v.label(), &*w.label()) { return false; }
|
|
iters_eq(&mut v.iter(), &mut w.iter())
|
|
}
|
|
CompoundClass::Sequence => {
|
|
iters_eq(&mut v.iter(), &mut w.iter())
|
|
}
|
|
CompoundClass::Set => {
|
|
let s1 = v.iter().collect::<BTreeSet<Value<D>>>();
|
|
let s2 = w.iter().collect::<BTreeSet<Value<D>>>();
|
|
s1 == s2
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
let d1 = v.entries().collect::<BTreeMap<Value<D>, Value<D>>>();
|
|
let d2 = w.entries().collect::<BTreeMap<Value<D>, Value<D>>>();
|
|
d1 == d2
|
|
}
|
|
}
|
|
ValueClass::Embedded => v.as_embedded().unwrap() == w.as_embedded().unwrap(),
|
|
}
|
|
}
|
|
|
|
// TODO: when unstable feature iter_order_by stabilises, use that instead
|
|
fn iters_cmp<D: Domain>(
|
|
i: &mut dyn Iterator<Item = Value<D>>,
|
|
j: &mut dyn Iterator<Item = Value<D>>,
|
|
) -> Ordering {
|
|
loop {
|
|
match i.next() {
|
|
None => return if j.next().is_none() { Ordering::Equal } else { Ordering::Less },
|
|
Some(ii) => match j.next() {
|
|
None => return Ordering::Greater,
|
|
Some(jj) => {
|
|
let r = value_cmp(&*ii, &*jj);
|
|
if !r.is_eq() { return r }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn value_cmp<D: Domain, V: ValueImpl<D> + ?Sized, W: ValueImpl<D> + ?Sized>(v: &V, w: &W) -> Ordering {
|
|
let cls = v.value_class();
|
|
cls.cmp(&w.value_class()).then_with(|| match cls {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean =>
|
|
v.as_boolean().cmp(&w.as_boolean()),
|
|
AtomClass::Float =>
|
|
cmp_f32(v.as_float().unwrap(), w.as_float().unwrap()),
|
|
AtomClass::Double =>
|
|
cmp_f64(v.as_double().unwrap(), w.as_double().unwrap()),
|
|
AtomClass::SignedInteger =>
|
|
v.as_signed_integer().cmp(&w.as_signed_integer()),
|
|
AtomClass::String =>
|
|
v.as_string().cmp(&w.as_string()),
|
|
AtomClass::ByteString =>
|
|
v.as_bytestring().cmp(&w.as_bytestring()),
|
|
AtomClass::Symbol =>
|
|
v.as_symbol().cmp(&w.as_symbol()),
|
|
},
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Record =>
|
|
value_cmp(&*v.label(), &*w.label()).then_with(
|
|
|| iters_cmp(&mut v.iter(), &mut w.iter())),
|
|
CompoundClass::Sequence => iters_cmp(&mut v.iter(), &mut w.iter()),
|
|
CompoundClass::Set => {
|
|
let s1 = v.iter().collect::<BTreeSet<_>>();
|
|
let s2 = w.iter().collect::<BTreeSet<_>>();
|
|
s1.cmp(&s2)
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
let d1 = v.entries().collect::<BTreeMap<_, _>>();
|
|
let d2 = w.entries().collect::<BTreeMap<_, _>>();
|
|
d1.cmp(&d2)
|
|
}
|
|
},
|
|
ValueClass::Embedded => v.as_embedded().unwrap().cmp(&w.as_embedded().unwrap()),
|
|
})
|
|
}
|
|
|
|
|
|
pub fn value_deepcopy<D: Domain>(v: &Value<D>) -> Value<D>
|
|
{
|
|
value_deepcopy_via(v, &mut |d| Result::<_,()>::Ok(Value::new(Embedded::new(d.clone())))).unwrap()
|
|
}
|
|
|
|
pub fn value_deepcopy_via<D: Domain, E: Domain, F, R: Into<Value<E>>, Err>(v: &Value<D>, f: &mut F) -> Result<Value<E>, Err>
|
|
where
|
|
F: FnMut(&D) -> Result<R, Err>
|
|
{
|
|
match v.value_class() {
|
|
ValueClass::Atomic(a) => Ok(match a {
|
|
AtomClass::Boolean => Value::new(v.as_boolean().unwrap()),
|
|
AtomClass::Float => Value::new(v.as_float().unwrap()),
|
|
AtomClass::Double => Value::new(v.as_double().unwrap()),
|
|
AtomClass::SignedInteger => Value::new(v.as_signed_integer().unwrap().into_owned()),
|
|
AtomClass::String => Value::new(v.as_string().unwrap().into_owned()),
|
|
AtomClass::ByteString => Value::new(Bytes::new(v.as_bytestring().unwrap().into_owned())),
|
|
AtomClass::Symbol => Value::new(Symbol::new(v.as_symbol().unwrap().into_owned())),
|
|
}),
|
|
ValueClass::Compound(c) => Ok(match c {
|
|
CompoundClass::Record => Value::new(Record::new(
|
|
value_deepcopy_via(&v.label(), f)?,
|
|
v.iter().map(|w| value_deepcopy_via(&w, f)).collect::<Result<Vec<_>, _>>()?)),
|
|
CompoundClass::Sequence => Value::new(
|
|
v.iter().map(|w| value_deepcopy_via(&w, f)).collect::<Result<Vec<_>, _>>()?),
|
|
CompoundClass::Set => Value::new(
|
|
v.iter().map(|w| value_deepcopy_via(&w, f)).collect::<Result<Set<_>, _>>()?),
|
|
CompoundClass::Dictionary => Value::new(
|
|
v.entries().map(|(k,w)| Ok((value_deepcopy_via(&k, f)?,
|
|
value_deepcopy_via(&w, f)?)))
|
|
.collect::<Result<Map<_, _>, _>>()?),
|
|
}),
|
|
ValueClass::Embedded => f(v.as_embedded().unwrap().as_ref()).map(|r| r.into()),
|
|
}
|
|
}
|
|
|
|
pub fn value_map_embedded<D: Domain, E: Domain, F, Err>(v: &Value<D>, f: &mut F) -> Result<Value<E>, Err>
|
|
where
|
|
F: FnMut(&D) -> Result<E, Err>
|
|
{
|
|
value_deepcopy_via(v, &mut |d| Ok(Value::new(Embedded::new(f(d)?))))
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! impl_value_methods {
|
|
({ $($gdecls:tt)* }, $t:ty) => {
|
|
impl< $($gdecls)* > std::fmt::Debug for $t {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
crate::TextWriter::fmt_value(f, &mut crate::DefaultDomainCodec, self)
|
|
.map_err(|_| std::fmt::Error)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > PartialEq for $t {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
crate::value_eq(self, other)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > Eq for $t {}
|
|
|
|
impl< $($gdecls)* > PartialOrd for $t {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(crate::value_cmp(self, other))
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > Ord for $t {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
crate::value_cmp(self, other)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > std::hash::Hash for $t {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
crate::value_hash(self, state)
|
|
}
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
impl_value_methods!({ D: Domain }, dyn ValueImpl<D>);
|
|
impl_value_methods!({ D: Domain }, Value<D>);
|