diff --git a/Cargo.lock b/Cargo.lock index 080747a..6c497c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,6 +197,7 @@ name = "merde_core" version = "6.1.0" dependencies = [ "compact_str", + "ordered-float", "rubicon", "rusqlite", "serde", @@ -269,6 +270,15 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" +[[package]] +name = "ordered-float" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d501f1a72f71d3c063a6bbc8f7271fa73aa09fe5d6283b6571e2ed176a2537" +dependencies = [ + "num-traits", +] + [[package]] name = "pkg-config" version = "0.3.30" diff --git a/merde/examples/yaml.rs b/merde/examples/yaml.rs index 1ff4593..f72b9cb 100644 --- a/merde/examples/yaml.rs +++ b/merde/examples/yaml.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; + +use merde::Value; use merde_core::Deserializer; use merde_yaml::YamlDeserializer; @@ -73,4 +76,16 @@ fn main() { let result: ComplexStruct = de.deserialize().unwrap(); println!("Deserialized ComplexStruct: {result:#?}"); + + let yaml_map = r#" + 1: 100 + "two": 200.5 + true: "three hundred" + [1, 2, 3]: { "nested": "value" } + "#; + + let mut de = YamlDeserializer::new(yaml_map); + let result: HashMap = de.deserialize().unwrap(); + + println!("Deserialized HashMap: {result:#?}"); } diff --git a/merde_core/Cargo.toml b/merde_core/Cargo.toml index 08a9350..8e9d0db 100644 --- a/merde_core/Cargo.toml +++ b/merde_core/Cargo.toml @@ -12,6 +12,7 @@ categories = ["encoding", "parser-implementations"] [dependencies] compact_str = { version = "0.8.0", optional = true } +ordered-float = "4.3.0" rubicon = "3.4.9" rusqlite = { version = "0.32.1", optional = true } serde = { version = "1", optional = true } diff --git a/merde_core/src/array.rs b/merde_core/src/array.rs index d635109..0beba6f 100644 --- a/merde_core/src/array.rs +++ b/merde_core/src/array.rs @@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut}; use crate::{value::Value, IntoStatic}; /// An array of [`Value`] items -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Eq, Hash, Clone)] #[repr(transparent)] pub struct Array<'s>(pub Vec>); diff --git a/merde_core/src/deserialize.rs b/merde_core/src/deserialize.rs index 7c0f8f1..32fe99f 100644 --- a/merde_core/src/deserialize.rs +++ b/merde_core/src/deserialize.rs @@ -562,7 +562,7 @@ impl<'s> Deserialize<'s> for Value<'s> { { match de.next()? { Event::Int(i) => Ok(Value::Int(i)), - Event::Float(f) => Ok(Value::Float(f)), + Event::Float(f) => Ok(Value::Float(f.into())), Event::Str(s) => Ok(Value::Str(s)), Event::Bool(b) => Ok(Value::Bool(b)), Event::Null => Ok(Value::Null), diff --git a/merde_core/src/map.rs b/merde_core/src/map.rs index 6c7103b..9e7aa2d 100644 --- a/merde_core/src/map.rs +++ b/merde_core/src/map.rs @@ -1,15 +1,25 @@ use std::{ collections::HashMap, + hash::{Hash, Hasher}, ops::{Deref, DerefMut}, }; use crate::{value::Value, CowStr, IntoStatic}; /// A map, dictionary, object, whatever — with string keys. -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Eq, Clone)] #[repr(transparent)] pub struct Map<'s>(pub HashMap, Value<'s>>); +impl Hash for Map<'_> { + fn hash(&self, state: &mut H) { + for (k, v) in self.iter() { + k.hash(state); + v.hash(state); + } + } +} + impl std::fmt::Debug for Map<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) diff --git a/merde_core/src/value.rs b/merde_core/src/value.rs index 87ddd88..5a9fce3 100644 --- a/merde_core/src/value.rs +++ b/merde_core/src/value.rs @@ -1,13 +1,15 @@ use std::{borrow::Cow, collections::HashMap}; +use ordered_float::OrderedFloat; + use crate::{array::Array, map::Map, CowStr, IntoStatic, MerdeError, ValueType}; /// Think [`serde_json::Value`](https://docs.rs/serde_json/1.0.128/serde_json/enum.Value.html), but with a small string optimization, /// copy-on-write strings, etc. Might include other value types later. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Value<'s> { Int(i64), - Float(f64), + Float(OrderedFloat), Str(CowStr<'s>), Bytes(Cow<'s, [u8]>), Null, @@ -42,7 +44,7 @@ impl<'s> From for Value<'s> { impl<'s> From for Value<'s> { fn from(v: f64) -> Self { - Value::Float(v) + Value::Float(v.into()) } } @@ -183,4 +185,15 @@ impl<'s> Value<'s> { }), } } + + #[inline(always)] + pub fn as_f64(&self) -> Result> { + match self { + Value::Float(n) => Ok(n.into_inner()), + _ => Err(MerdeError::MismatchedType { + expected: ValueType::Float, + found: self.value_type(), + }), + } + } } diff --git a/merde_json/src/lib.rs b/merde_json/src/lib.rs index aeaf3db..7185232 100644 --- a/merde_json/src/lib.rs +++ b/merde_json/src/lib.rs @@ -206,7 +206,7 @@ impl JsonSerialize for Value<'_> { Value::Null => serializer.write_null(), Value::Bool(b) => serializer.write_bool(*b), Value::Int(i) => serializer.write_i64(*i), - Value::Float(f) => serializer.write_f64(*f), + Value::Float(f) => serializer.write_f64(f.into_inner()), Value::Str(s) => serializer.write_str(s), Value::Bytes(_b) => { // TODO: we're going to need to make json_serialize return