diff --git a/Cargo.lock b/Cargo.lock index 6c497c0..8727207 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "castaway" version = "0.2.3" @@ -54,6 +60,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "compact_bytes" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b08f6a0d5ec37167557a0434307dcfbd7e6808b860492a5a626568a5522550e" +dependencies = [ + "serde", + "static_assertions", +] + [[package]] name = "compact_str" version = "0.8.0" @@ -188,6 +204,7 @@ dependencies = [ "ahash", "merde_core", "merde_json", + "merde_msgpack", "merde_time", "merde_yaml", ] @@ -196,6 +213,7 @@ dependencies = [ name = "merde_core" version = "6.1.0" dependencies = [ + "compact_bytes", "compact_str", "ordered-float", "rubicon", @@ -209,10 +227,27 @@ version = "6.1.0" dependencies = [ "lexical-parse-float", "merde_core", + "merde_loggingserializer", "num-bigint", "num-traits", ] +[[package]] +name = "merde_loggingserializer" +version = "0.1.0" +dependencies = [ + "merde_core", +] + +[[package]] +name = "merde_msgpack" +version = "7.1.0" +dependencies = [ + "merde_core", + "merde_loggingserializer", + "rmp", +] + [[package]] name = "merde_time" version = "4.0.15" @@ -279,6 +314,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pkg-config" version = "0.3.30" @@ -309,6 +350,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + [[package]] name = "rubicon" version = "3.4.9" diff --git a/Cargo.toml b/Cargo.toml index ff6bb0c..6233d80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,12 @@ [workspace] resolver = "2" -members = ["merde", "merde_json", "merde_time", "merde_core", "merde_yaml"] -exclude = ["zerodeps-example"] +members = [ + "merde", + "merde_json", + "merde_time", + "merde_core", + "merde_yaml", + "merde_msgpack", + "merde_loggingserializer", +] +exclude = ["zerodeps-example", "merde_msgpack/testdata-maker"] diff --git a/merde/Cargo.toml b/merde/Cargo.toml index 6172c50..d5c8a85 100644 --- a/merde/Cargo.toml +++ b/merde/Cargo.toml @@ -49,6 +49,7 @@ required-features = ["json", "ahash"] merde_core = { version = "6.1.0", path = "../merde_core", optional = true } merde_json = { version = "6.1.0", path = "../merde_json", optional = true } merde_yaml = { version = "7.1.0", path = "../merde_yaml", optional = true } +merde_msgpack = { version = "7.1.0", path = "../merde_msgpack", optional = true } merde_time = { version = "4.0.15", path = "../merde_time", optional = true, features = [ "merde", "deserialize", @@ -62,6 +63,7 @@ full = [ "deserialize", "json", "yaml", + "msgpack", "time", "rusqlite", "compact_str", @@ -76,6 +78,7 @@ rusqlite = ["merde_core/rusqlite"] # non-core crates json = ["dep:merde_json", "merde_time/json"] yaml = ["dep:merde_yaml"] +msgpack = ["dep:merde_msgpack"] time = ["dep:merde_time"] # others diff --git a/merde_core/Cargo.toml b/merde_core/Cargo.toml index 8e9d0db..94f10cd 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 } +compact_bytes = { version = "0.1.3", optional = true } ordered-float = "4.3.0" rubicon = "3.4.9" rusqlite = { version = "0.32.1", optional = true } @@ -19,7 +20,14 @@ serde = { version = "1", optional = true } [features] default = [] -full = ["compact_str", "serde", "rusqlite"] +full = [ + # (1 per line) + "compact_str", + "compact_bytes", + "serde", + "rusqlite", +] compact_str = ["dep:compact_str"] +compact_bytes = ["dep:compact_bytes"] serde = ["dep:serde", "compact_str/serde"] rusqlite = ["dep:rusqlite"] diff --git a/merde_core/src/cowbytes.rs b/merde_core/src/cowbytes.rs new file mode 100644 index 0000000..4d58909 --- /dev/null +++ b/merde_core/src/cowbytes.rs @@ -0,0 +1,154 @@ +use std::{ + borrow::Cow, + fmt, + hash::{Hash, Hasher}, + ops::Deref, +}; + +#[cfg(feature = "compact_bytes")] +use compact_bytes::CompactBytes; + +use crate::IntoStatic; + +/// A copy-on-write bytes type that uses [`CompactBytes`] for +/// the "owned" variant. +#[derive(Clone)] +pub enum CowBytes<'a> { + Borrowed(&'a [u8]), + #[cfg(feature = "compact_bytes")] + Owned(CompactBytes), + #[cfg(not(feature = "compact_bytes"))] + Owned(Vec), +} + +impl<'a> CowBytes<'a> { + pub fn new(bytes: &'a [u8]) -> Self { + CowBytes::Borrowed(bytes) + } + + pub fn into_owned(self) -> Vec { + match self { + CowBytes::Borrowed(b) => b.to_vec(), + #[cfg(feature = "compact_bytes")] + CowBytes::Owned(b) => b.to_vec(), + #[cfg(not(feature = "compact_bytes"))] + CowBytes::Owned(b) => b, + } + } +} + +impl AsRef<[u8]> for CowBytes<'_> { + fn as_ref(&self) -> &[u8] { + match self { + CowBytes::Borrowed(b) => b, + CowBytes::Owned(b) => b.as_ref(), + } + } +} + +impl Deref for CowBytes<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl<'a> From<&'a [u8]> for CowBytes<'a> { + fn from(b: &'a [u8]) -> Self { + CowBytes::Borrowed(b) + } +} + +impl<'a> From> for CowBytes<'a> { + fn from(v: Vec) -> Self { + #[cfg(feature = "compact_bytes")] + { + CowBytes::Owned(CompactBytes::new(v)) + } + #[cfg(not(feature = "compact_bytes"))] + { + CowBytes::Owned(v) + } + } +} + +impl<'a> From> for CowBytes<'a> { + fn from(cow: Cow<'a, [u8]>) -> Self { + match cow { + Cow::Borrowed(b) => CowBytes::Borrowed(b), + Cow::Owned(v) => v.into(), + } + } +} + +impl<'a, 'b> PartialEq> for CowBytes<'b> { + fn eq(&self, other: &CowBytes<'a>) -> bool { + self.deref() == other.deref() + } +} + +impl PartialEq<[u8]> for CowBytes<'_> { + fn eq(&self, other: &[u8]) -> bool { + self.deref() == other + } +} + +impl PartialEq> for [u8] { + fn eq(&self, other: &CowBytes<'_>) -> bool { + self == other.deref() + } +} + +impl Eq for CowBytes<'_> {} + +impl Hash for CowBytes<'_> { + fn hash(&self, state: &mut H) { + self.deref().hash(state) + } +} + +impl fmt::Debug for CowBytes<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.deref(), f) + } +} + +impl IntoStatic for CowBytes<'_> { + type Output = CowBytes<'static>; + + fn into_static(self) -> Self::Output { + match self { + #[cfg(feature = "compact_bytes")] + CowBytes::Borrowed(b) => CowBytes::Owned(CompactBytes::new(b)), + #[cfg(not(feature = "compact_bytes"))] + CowBytes::Borrowed(b) => CowBytes::Owned(b.to_vec()), + CowBytes::Owned(b) => CowBytes::Owned(b), + } + } +} + +#[cfg(feature = "serde")] +mod serde_impls { + use super::*; + use serde::{Deserialize, Serialize}; + + impl Serialize for CowBytes<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(self) + } + } + + impl<'de> Deserialize<'de> for CowBytes<'_> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes = Vec::::deserialize(deserializer)?; + Ok(CowBytes::from(bytes)) + } + } +} diff --git a/merde_core/src/deserialize.rs b/merde_core/src/deserialize.rs index 32fe99f..e04fc91 100644 --- a/merde_core/src/deserialize.rs +++ b/merde_core/src/deserialize.rs @@ -7,13 +7,15 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, }; -use crate::{Array, CowStr, IntoStatic, Map, MerdeError, Value, WithLifetime}; +use crate::{Array, CowBytes, CowStr, IntoStatic, Map, MerdeError, Value, WithLifetime}; #[derive(Debug)] pub enum Event<'s> { - Int(i64), + I64(i64), + U64(u64), Float(f64), Str(CowStr<'s>), + Bytes(CowBytes<'s>), Bool(bool), Null, MapStart, @@ -24,9 +26,11 @@ pub enum Event<'s> { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum EventType { - Int, + I64, + U64, Float, Str, + Bytes, Bool, Null, MapStart, @@ -38,9 +42,11 @@ pub enum EventType { impl From<&Event<'_>> for EventType { fn from(event: &Event<'_>) -> Self { match event { - Event::Int(_) => EventType::Int, + Event::I64(_) => EventType::I64, + Event::U64(_) => EventType::U64, Event::Float(_) => EventType::Float, Event::Str(_) => EventType::Str, + Event::Bytes(_) => EventType::Bytes, Event::Bool(_) => EventType::Bool, Event::Null => EventType::Null, Event::MapStart => EventType::MapStart, @@ -59,10 +65,20 @@ pub struct ArrayStart { impl<'s> Event<'s> { pub fn into_i64(self) -> Result> { match self { - Event::Int(i) => Ok(i), + Event::I64(i) => Ok(i), _ => Err(MerdeError::UnexpectedEvent { got: EventType::from(&self), - expected: &[EventType::Int], + expected: &[EventType::I64], + }), + } + } + + pub fn into_u64(self) -> Result> { + match self { + Event::U64(u) => Ok(u), + _ => Err(MerdeError::UnexpectedEvent { + got: EventType::from(&self), + expected: &[EventType::U64], }), } } @@ -87,6 +103,16 @@ impl<'s> Event<'s> { } } + pub fn into_bytes(self) -> Result, MerdeError<'static>> { + match self { + Event::Bytes(b) => Ok(b), + _ => Err(MerdeError::UnexpectedEvent { + got: EventType::from(&self), + expected: &[EventType::Bytes], + }), + } + } + pub fn into_bool(self) -> Result> { match self { Event::Bool(b) => Ok(b), @@ -249,12 +275,13 @@ impl<'s> Deserialize<'s> for i64 { D: Deserializer<'s> + ?Sized, { let v: i64 = match de.next()? { - Event::Int(i) => i, + Event::I64(i) => i, + Event::U64(u) => u.try_into().map_err(|_| MerdeError::OutOfRange)?, Event::Float(f) => f as _, ev => { return Err(MerdeError::UnexpectedEvent { got: EventType::from(&ev), - expected: &[EventType::Int, EventType::Float], + expected: &[EventType::I64, EventType::U64, EventType::Float], } .into()) } @@ -268,8 +295,19 @@ impl<'s> Deserialize<'s> for u64 { where D: Deserializer<'s> + ?Sized, { - let v: i64 = de.t().await?; - v.try_into().map_err(|_| MerdeError::OutOfRange.into()) + let v: u64 = match de.next()? { + Event::U64(u) => u, + Event::I64(i) => i.try_into().map_err(|_| MerdeError::OutOfRange)?, + Event::Float(f) => f as u64, + ev => { + return Err(MerdeError::UnexpectedEvent { + got: EventType::from(&ev), + expected: &[EventType::U64, EventType::I64, EventType::Float], + } + .into()) + } + }; + Ok(v) } } @@ -288,7 +326,7 @@ impl<'s> Deserialize<'s> for u32 { where D: Deserializer<'s> + ?Sized, { - let v: i64 = de.t().await?; + let v: u64 = de.t().await?; v.try_into().map_err(|_| MerdeError::OutOfRange.into()) } } @@ -308,7 +346,7 @@ impl<'s> Deserialize<'s> for u16 { where D: Deserializer<'s> + ?Sized, { - let v: i64 = de.t().await?; + let v: u64 = de.t().await?; v.try_into().map_err(|_| MerdeError::OutOfRange.into()) } } @@ -328,7 +366,7 @@ impl<'s> Deserialize<'s> for u8 { where D: Deserializer<'s> + ?Sized, { - let v: i64 = de.t().await?; + let v: u64 = de.t().await?; v.try_into().map_err(|_| MerdeError::OutOfRange.into()) } } @@ -348,7 +386,7 @@ impl<'s> Deserialize<'s> for usize { where D: Deserializer<'s> + ?Sized, { - let v: i64 = de.t().await?; + let v: u64 = de.t().await?; v.try_into().map_err(|_| MerdeError::OutOfRange.into()) } } @@ -369,11 +407,12 @@ impl<'s> Deserialize<'s> for f64 { { let v: f64 = match de.next()? { Event::Float(f) => f, - Event::Int(i) => i as f64, + Event::I64(i) => i as f64, + Event::U64(u) => u as f64, ev => { return Err(MerdeError::UnexpectedEvent { got: EventType::from(&ev), - expected: &[EventType::Float, EventType::Int], + expected: &[EventType::Float, EventType::I64, EventType::U64], } .into()) } @@ -561,9 +600,11 @@ impl<'s> Deserialize<'s> for Value<'s> { D: Deserializer<'s> + ?Sized, { match de.next()? { - Event::Int(i) => Ok(Value::Int(i)), + Event::I64(i) => Ok(Value::I64(i)), + Event::U64(u) => Ok(Value::U64(u)), Event::Float(f) => Ok(Value::Float(f.into())), Event::Str(s) => Ok(Value::Str(s)), + Event::Bytes(b) => Ok(Value::Bytes(b)), Event::Bool(b) => Ok(Value::Bool(b)), Event::Null => Ok(Value::Null), Event::MapStart => { @@ -602,9 +643,11 @@ impl<'s> Deserialize<'s> for Value<'s> { ev => Err(MerdeError::UnexpectedEvent { got: EventType::from(&ev), expected: &[ - EventType::Int, + EventType::I64, + EventType::U64, EventType::Float, EventType::Str, + EventType::Bytes, EventType::Bool, EventType::Null, EventType::MapStart, diff --git a/merde_core/src/error.rs b/merde_core/src/error.rs index e0a87d4..f86bfcf 100644 --- a/merde_core/src/error.rs +++ b/merde_core/src/error.rs @@ -15,10 +15,10 @@ pub enum ValueType { Bool, /// The value fits in an `i64`. - Int, + I64, - /// The value no longer fits in an `i64`. - BigInt, + /// The value fits in a `u64`. + U64, /// The value has decimal places. Float, @@ -179,7 +179,8 @@ impl Value<'_> { match self { Value::Null => ValueType::Null, Value::Bool(_) => ValueType::Bool, - Value::Int(_) => ValueType::Int, + Value::I64(_) => ValueType::I64, + Value::U64(_) => ValueType::U64, Value::Float(_) => ValueType::Float, Value::Str(_) => ValueType::String, Value::Bytes(_) => ValueType::Bytes, diff --git a/merde_core/src/lib.rs b/merde_core/src/lib.rs index d004178..253a4e3 100644 --- a/merde_core/src/lib.rs +++ b/merde_core/src/lib.rs @@ -1,6 +1,9 @@ mod cowstr; pub use cowstr::CowStr; +mod cowbytes; +pub use cowbytes::CowBytes; + mod array; pub use array::Array; @@ -33,4 +36,7 @@ rubicon::compatibility_check! { #[cfg(feature = "compact_str")] ("compact_str", "enabled") + + #[cfg(feature = "compact_bytes")] + ("compact_bytes", "enabled") } diff --git a/merde_core/src/value.rs b/merde_core/src/value.rs index 5a9fce3..d4a8b2b 100644 --- a/merde_core/src/value.rs +++ b/merde_core/src/value.rs @@ -1,17 +1,18 @@ -use std::{borrow::Cow, collections::HashMap}; +use std::collections::HashMap; use ordered_float::OrderedFloat; -use crate::{array::Array, map::Map, CowStr, IntoStatic, MerdeError, ValueType}; +use crate::{array::Array, map::Map, CowBytes, 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, Eq, Hash, Clone)] pub enum Value<'s> { - Int(i64), + I64(i64), + U64(u64), Float(OrderedFloat), Str(CowStr<'s>), - Bytes(Cow<'s, [u8]>), + Bytes(CowBytes<'s>), Null, Bool(bool), Array(Array<'s>), @@ -24,7 +25,8 @@ impl IntoStatic for Value<'_> { #[inline(always)] fn into_static(self) -> ::Output { match self { - Value::Int(i) => Value::Int(i), + Value::I64(i) => Value::I64(i), + Value::U64(u) => Value::U64(u), Value::Float(f) => Value::Float(f), Value::Str(s) => Value::Str(s.into_static()), Value::Bytes(b) => Value::Bytes(b.into_static()), @@ -38,7 +40,13 @@ impl IntoStatic for Value<'_> { impl<'s> From for Value<'s> { fn from(v: i64) -> Self { - Value::Int(v) + Value::I64(v) + } +} + +impl<'s> From for Value<'s> { + fn from(v: u64) -> Self { + Value::U64(v) } } @@ -175,12 +183,47 @@ impl<'s> Value<'s> { } } + #[inline(always)] + pub fn as_bytes(&self) -> Result<&CowBytes<'s>, MerdeError<'static>> { + match self { + Value::Bytes(b) => Ok(b), + _ => Err(MerdeError::MismatchedType { + expected: ValueType::Bytes, + found: self.value_type(), + }), + } + } + + #[inline(always)] + pub fn into_bytes(self) -> Result, MerdeError<'static>> { + match self { + Value::Bytes(b) => Ok(b), + _ => Err(MerdeError::MismatchedType { + expected: ValueType::Bytes, + found: self.value_type(), + }), + } + } + #[inline(always)] pub fn as_i64(&self) -> Result> { match self { - Value::Int(n) => Ok(*n), + Value::I64(n) => Ok(*n), + Value::U64(n) if *n <= i64::MAX as u64 => Ok(*n as i64), + _ => Err(MerdeError::MismatchedType { + expected: ValueType::I64, + found: self.value_type(), + }), + } + } + + #[inline(always)] + pub fn as_u64(&self) -> Result> { + match self { + Value::U64(n) => Ok(*n), + Value::I64(n) => Ok((*n).try_into().map_err(|_| MerdeError::OutOfRange)?), _ => Err(MerdeError::MismatchedType { - expected: ValueType::Int, + expected: ValueType::U64, found: self.value_type(), }), } diff --git a/merde_json/Cargo.toml b/merde_json/Cargo.toml index c889fd8..f225462 100644 --- a/merde_json/Cargo.toml +++ b/merde_json/Cargo.toml @@ -20,3 +20,6 @@ num-traits = { version = "0.2.19", optional = true } default = [] full = ["num-bigint"] num-bigint = ["dep:num-bigint", "dep:num-traits"] + +[dev-dependencies] +merde_loggingserializer = { path = "../merde_loggingserializer" } diff --git a/merde_json/src/deserialize.rs b/merde_json/src/deserialize.rs index eaac034..743fb1c 100644 --- a/merde_json/src/deserialize.rs +++ b/merde_json/src/deserialize.rs @@ -140,7 +140,7 @@ impl<'s> Deserializer<'s> for JsonDeserializer<'s> { .known_float(peek) .map_err(|err| jiter_error(self.source, err))?; if num.fract() == 0.0 && num >= i64::MIN as f64 && num <= i64::MAX as f64 { - Event::Int(num as i64) + Event::I64(num as i64) } else { Event::Float(num) } @@ -216,10 +216,7 @@ mod tests { use super::JsonDeserializer; use merde_core::{Array, CowStr, Deserialize, Deserializer, Event, EventType, Map, MerdeError}; - use std::{ - future::Future, - task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, - }; + use merde_loggingserializer::LoggingDeserializer; #[derive(Debug, PartialEq)] pub struct Sample { @@ -291,136 +288,50 @@ mod tests { "#; let deser = JsonDeserializer::new(input); - - struct LoggingDeserializer<'s, I> - where - I: Deserializer<'s>, - { - inner: I, - starter: Option>, - } - - impl<'s, I> std::fmt::Debug for LoggingDeserializer<'s, I> - where - I: Deserializer<'s>, - { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LoggingDeserializer") - .field("inner", &self.inner) - .finish() - } - } - - impl<'s, I> LoggingDeserializer<'s, I> - where - I: Deserializer<'s>, - { - fn new(inner: I) -> Self { - Self { - inner, - starter: None, - } - } - } - - impl<'s, I> Deserializer<'s> for LoggingDeserializer<'s, I> - where - I: Deserializer<'s>, - { - type Error<'es> = I::Error<'es>; - - fn next(&mut self) -> Result, Self::Error<'s>> { - if let Some(ev) = self.starter.take() { - eprintln!("> (from starter) {:?}", ev); - return Ok(ev); - } - - let ev = self.inner.next()?; - Ok(ev) - } - - async fn t_starting_with>( - &mut self, - starter: Option>, - ) -> Result> { - if let Some(starter) = starter { - if self.starter.is_some() { - unreachable!("setting starter when it's already set? shouldn't happen") - } - self.starter = Some(starter); - } - - T::deserialize(self).await - } - } - let mut deser = LoggingDeserializer::new(deser); - let fut = deser.t::>(); - let fut = std::pin::pin!(fut); - let vtable = RawWakerVTable::new(|_| todo!(), |_| {}, |_| {}, |_| {}); - let vtable = Box::leak(Box::new(vtable)); - let w = unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), vtable)) }; - let mut cx = Context::from_waker(&w); - match fut.poll(&mut cx) { - Poll::Ready(res) => { - let samples = res.unwrap(); - assert_eq!( - samples, - vec![ - Sample { - height: 100, - kind: true - }, - Sample { - height: 200, - kind: false - }, - Sample { - height: 150, - kind: true - } - ] - ); - } - _ => panic!("returned poll pending for some reason?"), - } - - let mut deser = JsonDeserializer::new(input); - let fut = deser.t::(); - let fut = std::pin::pin!(fut); - let vtable = RawWakerVTable::new(|_| todo!(), |_| {}, |_| {}, |_| {}); - let vtable = Box::leak(Box::new(vtable)); - let w = unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), vtable)) }; - let mut cx = Context::from_waker(&w); - match fut.poll(&mut cx) { - Poll::Ready(res) => { - let value = res.unwrap(); - eprintln!("value = {:#?}", value); - - assert_eq!( - value, - Array::new() - .with( - Map::new() - .with("height", merde_core::Value::Int(100)) - .with("kind", merde_core::Value::Bool(true)) - ) - .with( - Map::new() - .with("height", merde_core::Value::Int(200)) - .with("kind", merde_core::Value::Bool(false)) - ) - .with( - Map::new() - .with("height", merde_core::Value::Int(150)) - .with("kind", merde_core::Value::Bool(true)) - ) - .into() - ); - } - _ => panic!("returned poll pending for some reason?"), - } + let samples = deser.deserialize::>().unwrap(); + assert_eq!( + samples, + vec![ + Sample { + height: 100, + kind: true + }, + Sample { + height: 200, + kind: false + }, + Sample { + height: 150, + kind: true + } + ] + ); + + let value = deser.deserialize::().unwrap(); + eprintln!("value = {:#?}", value); + + assert_eq!( + value, + Array::new() + .with( + Map::new() + .with("height", merde_core::Value::I64(100)) + .with("kind", merde_core::Value::Bool(true)) + ) + .with( + Map::new() + .with("height", merde_core::Value::I64(200)) + .with("kind", merde_core::Value::Bool(false)) + ) + .with( + Map::new() + .with("height", merde_core::Value::I64(150)) + .with("kind", merde_core::Value::Bool(true)) + ) + .into() + ); } #[test] diff --git a/merde_json/src/lib.rs b/merde_json/src/lib.rs index 7185232..440d655 100644 --- a/merde_json/src/lib.rs +++ b/merde_json/src/lib.rs @@ -205,7 +205,8 @@ impl JsonSerialize for Value<'_> { match self { Value::Null => serializer.write_null(), Value::Bool(b) => serializer.write_bool(*b), - Value::Int(i) => serializer.write_i64(*i), + Value::I64(i) => serializer.write_i64(*i), + Value::U64(u) => serializer.write_i64(*u as i64), Value::Float(f) => serializer.write_f64(f.into_inner()), Value::Str(s) => serializer.write_str(s), Value::Bytes(_b) => { diff --git a/merde_loggingserializer/Cargo.toml b/merde_loggingserializer/Cargo.toml new file mode 100644 index 0000000..9076d6a --- /dev/null +++ b/merde_loggingserializer/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "merde_loggingserializer" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +merde_core = { version = "6.1.0", path = "../merde_core" } diff --git a/merde_loggingserializer/src/lib.rs b/merde_loggingserializer/src/lib.rs new file mode 100644 index 0000000..1d5527e --- /dev/null +++ b/merde_loggingserializer/src/lib.rs @@ -0,0 +1,64 @@ +use merde_core::{Deserialize, Deserializer, Event}; + +pub struct LoggingDeserializer<'s, I> +where + I: Deserializer<'s>, +{ + inner: I, + starter: Option>, +} + +impl<'s, I> std::fmt::Debug for LoggingDeserializer<'s, I> +where + I: Deserializer<'s>, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LoggingDeserializer") + .field("inner", &self.inner) + .finish() + } +} + +impl<'s, I> LoggingDeserializer<'s, I> +where + I: Deserializer<'s>, +{ + pub fn new(inner: I) -> Self { + Self { + inner, + starter: None, + } + } +} + +impl<'s, I> Deserializer<'s> for LoggingDeserializer<'s, I> +where + I: Deserializer<'s>, +{ + type Error<'es> = I::Error<'es>; + + fn next(&mut self) -> Result, Self::Error<'s>> { + if let Some(ev) = self.starter.take() { + eprintln!("> (from starter) {:?}", ev); + return Ok(ev); + } + + let ev = self.inner.next()?; + eprintln!("> (from inner.next) {:?}", ev); + Ok(ev) + } + + async fn t_starting_with>( + &mut self, + starter: Option>, + ) -> Result> { + if let Some(starter) = starter { + if self.starter.is_some() { + unreachable!("setting starter when it's already set? shouldn't happen") + } + self.starter = Some(starter); + } + + T::deserialize(self).await + } +} diff --git a/merde_msgpack/Cargo.toml b/merde_msgpack/Cargo.toml new file mode 100644 index 0000000..a367793 --- /dev/null +++ b/merde_msgpack/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "merde_msgpack" +version = "7.1.0" +edition = "2021" +authors = ["Amos Wenger "] +description = "msgpack serizliation/deserialization for merde" +license = "Apache-2.0 OR MIT" +readme = "README.md" +repository = "https://github.com/bearcove/merde" +keywords = ["msgpack", "messagepack", "serialization", "deserialization"] +categories = ["encoding", "parser-implementations"] + +[dependencies] +merde_core = { version = "6.1.0", path = "../merde_core" } +rmp = "0.8.14" + +[dev-dependencies] +merde_loggingserializer = { path = "../merde_loggingserializer" } diff --git a/merde_msgpack/Justfile b/merde_msgpack/Justfile new file mode 100644 index 0000000..dc6989a --- /dev/null +++ b/merde_msgpack/Justfile @@ -0,0 +1,3 @@ + +regen: + cargo run --manifest-path testdata-maker/Cargo.toml diff --git a/merde_msgpack/README.md b/merde_msgpack/README.md new file mode 100644 index 0000000..dcc0461 --- /dev/null +++ b/merde_msgpack/README.md @@ -0,0 +1,15 @@ +[![license: MIT/Apache-2.0](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT) +[![crates.io](https://img.shields.io/crates/v/merde_rmp.svg)](https://crates.io/crates/merde_rmp) +[![docs.rs](https://docs.rs/merde_rmp/badge.svg)](https://docs.rs/merde_rmp) + +# merde_rmp + +![The merde logo: a glorious poop floating above a pair of hands](https://github.com/user-attachments/assets/763d60e0-5101-48af-bc72-f96f516a5d0f) + +_Logo by [MisiasArt](https://misiasart.com)_ + +Adds RMP serialization/deserialization support for +[merde](https://crates.io/crates/merde). + +You would normally add a dependency on [merde](https://crates.io/crates/merde) +directly, enabling its `rmp` feature. diff --git a/merde_msgpack/src/lib.rs b/merde_msgpack/src/lib.rs new file mode 100644 index 0000000..beb19ee --- /dev/null +++ b/merde_msgpack/src/lib.rs @@ -0,0 +1,406 @@ +#![deny(missing_docs)] +#![doc = include_str!("../README.md")] + +use merde_core::{Deserialize, DeserializeOwned, Deserializer}; + +/// A MessagePack deserializer, that implements [`merde_core::Deserializer`]. +pub struct MsgpackDeserializer<'s> { + source: &'s [u8], + offset: usize, + stack: Vec, +} + +#[derive(Debug)] +enum StackItem { + Array(usize), + Map(usize), +} + +impl<'s> MsgpackDeserializer<'s> { + /// Construct a new MessagePack deserializer + pub fn new(source: &'s [u8]) -> Self { + Self { + source, + offset: 0, + stack: Vec::new(), + } + } +} + +impl std::fmt::Debug for MsgpackDeserializer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MsgpackDeserializer") + .field("source_len", &self.source.len()) + .field("stack", &self.stack) + .finish() + } +} + +/// Unifies [`merde_core::MerdeError`], and our own parsing errors. +pub enum MsgpackError<'s> { + /// Most likely an error encountered when "deserializing" the MessagePack data. + MerdeError(merde_core::MerdeError<'s>), + + /// An error encountered when reading the MessagePack data. + DecodeError(&'static str), + + /// Unsupported MessagePack type encountered + UnsupportedType(u8), + + /// EOF encountered while expecting a value + Eof, + + /// Unsupported fixext or ext type encountered + UnsupportedExtType(u8), +} + +impl std::fmt::Debug for MsgpackError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MsgpackError::MerdeError(e) => write!(f, "MerdeError({:?})", e), + MsgpackError::DecodeError(msg) => write!(f, "DecodeError({})", msg), + MsgpackError::UnsupportedType(t) => write!(f, "UnsupportedType(0x{:02x})", t), + MsgpackError::Eof => write!(f, "Eof"), + MsgpackError::UnsupportedExtType(t) => write!(f, "UnsupportedExtType(0x{:02x})", t), + } + } +} + +impl<'s> From> for MsgpackError<'s> { + fn from(e: merde_core::MerdeError<'s>) -> MsgpackError<'s> { + MsgpackError::MerdeError(e) + } +} + +impl<'s> merde_core::Deserializer<'s> for MsgpackDeserializer<'s> { + type Error<'es> = MsgpackError<'es>; + + fn next(&mut self) -> Result, Self::Error<'s>> { + if let Some(stack_item) = self.stack.last_mut() { + match stack_item { + StackItem::Array(count) => { + if *count == 0 { + self.stack.pop(); + return Ok(merde_core::Event::ArrayEnd); + } + *count -= 1; + } + StackItem::Map(count) => { + if *count == 0 { + self.stack.pop(); + return Ok(merde_core::Event::MapEnd); + } + *count -= 1; + } + } + } + + if self.offset >= self.source.len() { + return Err(MsgpackError::Eof); + } + + let byte = self.source[self.offset]; + self.offset += 1; + + match byte { + 0xc0 => Ok(merde_core::Event::Null), + 0xc2 => Ok(merde_core::Event::Bool(false)), + 0xc3 => Ok(merde_core::Event::Bool(true)), + 0xc4 => self.read_bytes_8(), + 0xc5 => self.read_bytes_16(), + 0xc6 => self.read_bytes_32(), + 0xcc => self.read_u8().map(|v| merde_core::Event::U64(v as u64)), + 0xcd => self.read_u16().map(|v| merde_core::Event::U64(v as u64)), + 0xce => self.read_u32().map(|v| merde_core::Event::U64(v as u64)), + 0xcf => self.read_u64().map(merde_core::Event::U64), + 0xd0 => self.read_i8().map(|v| merde_core::Event::I64(v as i64)), + 0xd1 => self.read_i16().map(|v| merde_core::Event::I64(v as i64)), + 0xd2 => self.read_i32().map(|v| merde_core::Event::I64(v as i64)), + 0xd3 => self.read_i64().map(merde_core::Event::I64), + 0xca => self.read_f32().map(|v| merde_core::Event::Float(v as f64)), + 0xcb => self.read_f64().map(merde_core::Event::Float), + 0xa0..=0xbf => { + let len = (byte & 0x1f) as usize; + self.read_str(len) + } + 0xd9 => self.read_str_8(), + 0xda => self.read_str_16(), + 0xdb => self.read_str_32(), + 0x90..=0x9f => { + let len = (byte & 0x0f) as usize; + self.stack.push(StackItem::Array(len)); + Ok(merde_core::Event::ArrayStart(merde_core::ArrayStart { + size_hint: Some(len), + })) + } + 0xdc => self.read_array_16(), + 0xdd => self.read_array_32(), + 0x80..=0x8f => { + let len = (byte & 0x0f) as usize; + self.stack.push(StackItem::Map(len * 2)); + Ok(merde_core::Event::MapStart) + } + 0xde => { + let len = self.read_u16()?; + self.stack.push(StackItem::Map(len as usize * 2)); + Ok(merde_core::Event::MapStart) + } + 0xdf => { + let len = self.read_u32()?; + self.stack.push(StackItem::Map(len as usize * 2)); + Ok(merde_core::Event::MapStart) + } + 0x00..=0x7f => Ok(merde_core::Event::U64(byte as u64)), + 0xe0..=0xff => Ok(merde_core::Event::I64((byte as i8) as i64)), + 0xd4..=0xd8 | 0xc7..=0xc9 => Err(MsgpackError::UnsupportedExtType(byte)), + _ => Err(MsgpackError::UnsupportedType(byte)), + } + } + + async fn t_starting_with>( + &mut self, + _starter: Option>, + ) -> Result> { + T::deserialize(self).await + } +} + +impl<'s> MsgpackDeserializer<'s> { + fn read_u8(&mut self) -> Result> { + if self.offset + 1 > self.source.len() { + return Err(MsgpackError::Eof); + } + let value = self.source[self.offset]; + self.offset += 1; + Ok(value) + } + + fn read_u16(&mut self) -> Result> { + if self.offset + 2 > self.source.len() { + return Err(MsgpackError::Eof); + } + let value = u16::from_be_bytes([self.source[self.offset], self.source[self.offset + 1]]); + self.offset += 2; + Ok(value) + } + + fn read_u32(&mut self) -> Result> { + if self.offset + 4 > self.source.len() { + return Err(MsgpackError::Eof); + } + let value = u32::from_be_bytes([ + self.source[self.offset], + self.source[self.offset + 1], + self.source[self.offset + 2], + self.source[self.offset + 3], + ]); + self.offset += 4; + Ok(value) + } + + fn read_u64(&mut self) -> Result> { + if self.offset + 8 > self.source.len() { + return Err(MsgpackError::Eof); + } + let value = u64::from_be_bytes([ + self.source[self.offset], + self.source[self.offset + 1], + self.source[self.offset + 2], + self.source[self.offset + 3], + self.source[self.offset + 4], + self.source[self.offset + 5], + self.source[self.offset + 6], + self.source[self.offset + 7], + ]); + self.offset += 8; + Ok(value) + } + + fn read_i8(&mut self) -> Result> { + self.read_u8().map(|v| v as i8) + } + + fn read_i16(&mut self) -> Result> { + self.read_u16().map(|v| v as i16) + } + + fn read_i32(&mut self) -> Result> { + self.read_u32().map(|v| v as i32) + } + + fn read_i64(&mut self) -> Result> { + self.read_u64().map(|v| v as i64) + } + + fn read_f32(&mut self) -> Result> { + self.read_u32().map(f32::from_bits) + } + + fn read_f64(&mut self) -> Result> { + self.read_u64().map(f64::from_bits) + } + + fn read_str(&mut self, len: usize) -> Result, MsgpackError<'s>> { + if self.offset + len > self.source.len() { + return Err(MsgpackError::Eof); + } + let s = std::str::from_utf8(&self.source[self.offset..self.offset + len]) + .map_err(|_| MsgpackError::DecodeError("Invalid UTF-8 string"))?; + self.offset += len; + Ok(merde_core::Event::Str(s.into())) + } + + fn read_str_8(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u8()? as usize; + self.read_str(len) + } + + fn read_str_16(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u16()? as usize; + self.read_str(len) + } + + fn read_str_32(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u32()? as usize; + self.read_str(len) + } + + fn read_bytes(&mut self, len: usize) -> Result, MsgpackError<'s>> { + if self.offset + len > self.source.len() { + return Err(MsgpackError::Eof); + } + let bytes = &self.source[self.offset..self.offset + len]; + self.offset += len; + Ok(merde_core::Event::Bytes(bytes.into())) + } + + fn read_bytes_8(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u8()? as usize; + self.read_bytes(len) + } + + fn read_bytes_16(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u16()? as usize; + self.read_bytes(len) + } + + fn read_bytes_32(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u32()? as usize; + self.read_bytes(len) + } + + fn read_array_16(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u16()? as usize; + self.stack.push(StackItem::Array(len)); + Ok(merde_core::Event::ArrayStart(merde_core::ArrayStart { + size_hint: Some(len), + })) + } + + fn read_array_32(&mut self) -> Result, MsgpackError<'s>> { + let len = self.read_u32()? as usize; + self.stack.push(StackItem::Array(len)); + Ok(merde_core::Event::ArrayStart(merde_core::ArrayStart { + size_hint: Some(len), + })) + } +} + +/// Deserialize an instance of type `T` from a byte slice of MessagePack data. +pub fn from_slice<'s, T>(slice: &'s [u8]) -> Result> +where + T: Deserialize<'s>, +{ + let mut deser = MsgpackDeserializer::new(slice); + deser.deserialize::() +} + +/// Deserialize an instance of type `T` from a byte slice of MessagePack data, +/// and return its static variant e.g. (CowStr<'static>, etc.) +pub fn from_slice_owned(slice: &[u8]) -> Result> +where + T: DeserializeOwned, +{ + let mut deser = MsgpackDeserializer::new(slice); + T::deserialize_owned(&mut deser) +} + +#[cfg(test)] +mod tests { + use merde_core::Array; + use merde_core::Deserializer; + use merde_core::Value; + use merde_loggingserializer::LoggingDeserializer; + + // cf. `testdata-maker/src/main.rs` + // regen with `just regen` + static TEST_INPUT: &[u8] = include_bytes!("../testdata/test.msgpack"); + + #[test] + fn test_deserialize() { + let deser = super::MsgpackDeserializer::new(TEST_INPUT); + let mut deser = LoggingDeserializer::new(deser); + + let value = deser.deserialize::().unwrap(); + + let array = value.as_array().unwrap(); + let mut iter = array.iter(); + + assert_eq!(iter.next().unwrap(), &Value::Null); + assert_eq!(iter.next().unwrap(), &Value::Bool(false)); + assert_eq!(iter.next().unwrap(), &Value::Bool(true)); + assert_eq!(iter.next().unwrap().as_u64().unwrap(), 42); + assert_eq!(iter.next().unwrap().as_i64().unwrap(), -123); + assert_eq!(iter.next().unwrap().as_u64().unwrap(), 1000000); + assert_eq!(iter.next().unwrap().as_i64().unwrap(), -9876543210); + assert_eq!(iter.next().unwrap().as_u64().unwrap(), 18446744073709551615); + assert!((iter.next().unwrap().as_f64().unwrap() - 1.23456).abs() < 1e-5); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), 0.0); + let value = iter.next().unwrap(); + assert!(value.as_f64().unwrap().is_infinite()); + assert!(value.as_f64().unwrap().is_sign_positive()); + let value = iter.next().unwrap(); + assert!(value.as_f64().unwrap().is_infinite()); + assert!(value.as_f64().unwrap().is_sign_negative()); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), f32::MIN as f64); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), f32::MAX as f64); + assert!((iter.next().unwrap().as_f64().unwrap() - 1.23456789).abs() < 1e-8); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), 0.0); + let value = iter.next().unwrap(); + assert!(value.as_f64().unwrap().is_infinite()); + assert!(value.as_f64().unwrap().is_sign_positive()); + let value = iter.next().unwrap(); + assert!(value.as_f64().unwrap().is_infinite()); + assert!(value.as_f64().unwrap().is_sign_negative()); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), f64::MIN); + assert_eq!(iter.next().unwrap().as_f64().unwrap(), f64::MAX); + assert!((iter.next().unwrap().as_f64().unwrap() - 1e-100).abs() < 1e-101); + assert!((iter.next().unwrap().as_f64().unwrap() - 1e100).abs() < 1e99); + assert_eq!( + iter.next().unwrap().as_str().unwrap().as_ref(), + "Hello, MessagePack!" + ); + assert_eq!(iter.next().unwrap().as_bytes().unwrap(), &[][..]); + assert_eq!( + iter.next().unwrap().as_bytes().unwrap(), + &[0xDE, 0xAD, 0xBE, 0xEF][..] + ); + assert_eq!( + iter.next().unwrap().as_bytes().unwrap(), + &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08][..] + ); + assert_eq!(iter.next().unwrap().as_bytes().unwrap(), &[0xFF; 256][..]); + assert!(iter.next().unwrap().as_array().unwrap().is_empty()); + assert_eq!( + iter.next().unwrap().as_array().unwrap(), + &Array(vec![Value::Null, Value::Bool(true)]) + ); + + let map = iter.next().unwrap().as_map().unwrap(); + assert_eq!(map.len(), 2); + assert_eq!(map.get(&"key1".into()).unwrap().as_u64().unwrap(), 1); + assert!((map.get(&"key2".into()).unwrap().as_f64().unwrap() - 2.7118).abs() < 1e-4); + + assert!(iter.next().unwrap().as_map().unwrap().is_empty()); + } +} diff --git a/merde_msgpack/testdata-maker/.gitignore b/merde_msgpack/testdata-maker/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/merde_msgpack/testdata-maker/.gitignore @@ -0,0 +1 @@ +/target diff --git a/merde_msgpack/testdata-maker/Cargo.lock b/merde_msgpack/testdata-maker/Cargo.lock new file mode 100644 index 0000000..270bed6 --- /dev/null +++ b/merde_msgpack/testdata-maker/Cargo.lock @@ -0,0 +1,58 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58450723cd9ee93273ce44a20b6ec4efe17f8ed2e3631474387bfdecf18bb2a9" +dependencies = [ + "num-traits", + "rmp", +] + +[[package]] +name = "testdata-maker" +version = "0.1.0" +dependencies = [ + "rmpv", +] diff --git a/merde_msgpack/testdata-maker/Cargo.toml b/merde_msgpack/testdata-maker/Cargo.toml new file mode 100644 index 0000000..479d3a1 --- /dev/null +++ b/merde_msgpack/testdata-maker/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "testdata-maker" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +rmpv = "1.3.0" diff --git a/merde_msgpack/testdata-maker/src/main.rs b/merde_msgpack/testdata-maker/src/main.rs new file mode 100644 index 0000000..1bb0793 --- /dev/null +++ b/merde_msgpack/testdata-maker/src/main.rs @@ -0,0 +1,52 @@ +use rmpv::Value; +use std::fs::File; +use std::io::Write; + +fn generate_test_messagepack() -> Vec { + let value = Value::Array(vec![ + Value::Nil, + Value::Boolean(false), + Value::Boolean(true), + Value::Integer(42.into()), + Value::Integer((-123).into()), + Value::Integer(1000000.into()), + Value::Integer((-9876543210i64).into()), + Value::Integer(18446744073709551615u64.into()), + Value::F32(1.23456), + Value::F32(0.0), + Value::F32(f32::INFINITY), + Value::F32(f32::NEG_INFINITY), + Value::F32(f32::MIN), + Value::F32(f32::MAX), + Value::F64(1.23456789), + Value::F64(0.0), + Value::F64(f64::INFINITY), + Value::F64(f64::NEG_INFINITY), + Value::F64(f64::MIN), + Value::F64(f64::MAX), + Value::F64(1e-100), + Value::F64(1e100), + Value::String("Hello, MessagePack!".into()), + Value::Binary(vec![]), + Value::Binary(vec![0xDE, 0xAD, 0xBE, 0xEF]), + Value::Binary(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), + Value::Binary(vec![0xFF; 256]), + Value::Array(vec![]), + Value::Array(vec![Value::Nil, Value::Boolean(true)]), + Value::Map(vec![ + (Value::String("key1".into()), Value::Integer(1.into())), + (Value::String("key2".into()), Value::F64(2.7118)), + ]), + Value::Map(vec![]), + ]); + + let mut buf = Vec::new(); + rmpv::encode::write_value(&mut buf, &value).unwrap(); + buf +} + +fn main() { + let encoded = generate_test_messagepack(); + let mut file = File::create("testdata/test.msgpack").unwrap(); + file.write_all(&encoded).unwrap(); +} diff --git a/merde_msgpack/testdata/test.msgpack b/merde_msgpack/testdata/test.msgpack new file mode 100644 index 0000000..97a5cfd Binary files /dev/null and b/merde_msgpack/testdata/test.msgpack differ diff --git a/merde_yaml/src/lib.rs b/merde_yaml/src/lib.rs index d7086c2..af51b74 100644 --- a/merde_yaml/src/lib.rs +++ b/merde_yaml/src/lib.rs @@ -108,7 +108,7 @@ impl<'s> Deserializer<'s> for YamlDeserializer<'s> { }), }, "int" => match s.parse::() { - Ok(v) => Ok(Event::Int(v)), + Ok(v) => Ok(Event::I64(v)), Err(_) => Err(MerdeYamlError::ParseError { expected_type: "int", }), @@ -135,7 +135,7 @@ impl<'s> Deserializer<'s> for YamlDeserializer<'s> { if let Ok(v) = s.parse::() { Ok(Event::Bool(v)) } else if let Ok(v) = s.parse::() { - Ok(Event::Int(v)) + Ok(Event::I64(v)) } else if let Ok(v) = s.parse::() { Ok(Event::Float(v)) } else if s == "~" || s == "null" {