Skip to content

Commit

Permalink
feat: implement Eq for Ipld
Browse files Browse the repository at this point in the history
Implement the `Eq` trait for the `Ipld` enum. NaN floats are never equal
to another. Though in the Ipld Data model they are forbidden. This means
that we can implement `Eq` for it.

As it's currently not strictly enforced that an `Ipld::Float` does not
contain a NaN, we make at least sure that in case we would encounter it,
that it is treated as equal.

For more information about the dicussion about this topic, see
ipld/libipld#171.
  • Loading branch information
vmx committed Mar 27, 2024
1 parent 612f1ec commit 12084a0
Showing 1 changed file with 34 additions and 1 deletion.
35 changes: 34 additions & 1 deletion src/ipld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl fmt::Display for IndexError {
impl std::error::Error for IndexError {}

/// Ipld
#[derive(Clone, PartialEq)]
#[derive(Clone)]
pub enum Ipld {
/// Represents the absence of a value or the value undefined.
Null,
Expand Down Expand Up @@ -86,6 +86,31 @@ impl fmt::Debug for Ipld {
}
}

/// NaN floats are forbidden in the IPLD Data Model, but we do not enforce it. So in case such a
/// value is introduced accidentally, make sure that it still compares as equal. This allows us
/// to implement `Eq` for `Ipld`.
impl PartialEq for Ipld {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Null, Self::Null) => true,
(Self::Bool(self_value), Self::Bool(other_value)) => self_value == other_value,
(Self::Integer(self_value), Self::Integer(other_value)) => self_value == other_value,
(Self::Float(self_value), Self::Float(other_value)) => {
// Treat two NaNs as being equal.
self_value == other_value || self_value.is_nan() && other_value.is_nan()
}
(Self::String(self_value), Self::String(other_value)) => self_value == other_value,
(Self::Bytes(self_value), Self::Bytes(other_value)) => self_value == other_value,
(Self::List(self_value), Self::List(other_value)) => self_value == other_value,
(Self::Map(self_value), Self::Map(other_value)) => self_value == other_value,
(Self::Link(self_value), Self::Link(other_value)) => self_value == other_value,
_ => false,
}
}
}

impl Eq for Ipld {}

/// IPLD Kind information without the actual value.
///
/// Sometimes it's useful to know the kind of an Ipld object without the actual value, e.g. for
Expand Down Expand Up @@ -363,4 +388,12 @@ mod tests {
let ipld = Ipld::Map(map);
assert_eq!(ipld.get("a").unwrap(), Some(&Ipld::Integer(0)));
}

// NaN floats are forbidden in the IPLD Data Model, but still make sure they are treated as
// equal in case they accidentally end up there.
#[test]
fn test_partial_eq_nan() {
let invalid_ipld = Ipld::Float(f64::NAN);
assert_eq!(invalid_ipld, invalid_ipld);
}
}

0 comments on commit 12084a0

Please sign in to comment.