diff --git a/Cargo.lock b/Cargo.lock index 6cae91a..b888222 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "ar_archive_writer" version = "0.2.0" @@ -407,7 +416,7 @@ dependencies = [ [[package]] name = "axum_duper" -version = "0.4.2" +version = "0.4.3" dependencies = [ "axum", "duper", @@ -934,6 +943,20 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +[[package]] +name = "decorum" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcc6bb0c903f3f815b48365bb9182092866f54a132d8ebe906f0b66952936e3" +dependencies = [ + "approx", + "num-traits", + "rustversion", + "serde", + "serde_derive", + "thiserror 2.0.17", +] + [[package]] name = "diff" version = "0.1.13" @@ -968,11 +991,12 @@ checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" [[package]] name = "duper" -version = "0.5.0" +version = "0.6.0" dependencies = [ "ariadne", "base64", "chumsky", + "decorum", "either", "indexmap", "insta", @@ -1008,7 +1032,7 @@ dependencies = [ [[package]] name = "duper-python" -version = "0.4.2" +version = "0.4.3" dependencies = [ "duper", "pyo3", @@ -1016,7 +1040,7 @@ dependencies = [ [[package]] name = "duper_lsp" -version = "0.1.3" +version = "0.1.4" dependencies = [ "async-lsp", "base64", @@ -1078,9 +1102,10 @@ dependencies = [ [[package]] name = "duperq" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", + "approx", "async-trait", "chumsky", "clap", @@ -2738,7 +2763,7 @@ dependencies = [ [[package]] name = "serde_duper" -version = "0.4.2" +version = "0.4.3" dependencies = [ "bytes", "chrono", @@ -3396,7 +3421,7 @@ dependencies = [ [[package]] name = "tracing_duper" -version = "0.1.0" +version = "0.1.1" dependencies = [ "chrono", "duper", diff --git a/axum_duper/CHANGELOG.md b/axum_duper/CHANGELOG.md index 2949588..57698a4 100644 --- a/axum_duper/CHANGELOG.md +++ b/axum_duper/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.4.2 (2025-12-07) ### Changed diff --git a/axum_duper/Cargo.toml b/axum_duper/Cargo.toml index 8d219b0..b79ac9e 100644 --- a/axum_duper/Cargo.toml +++ b/axum_duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axum_duper" description = "Axum extractor/response for Duper." -version = "0.4.2" +version = "0.4.3" edition = "2024" rust-version = "1.88" license = "MIT" @@ -15,7 +15,7 @@ keywords = ["duper", "serde", "axum"] [dependencies] axum = { version = "0.8", default-features = false } serde_core = "1" -duper = { path = "../duper", version = "0.5", features = ["serde"] } +duper = { path = "../duper", version = "0.6", features = ["serde"] } [dev-dependencies] serde = { version = "1.0.228", features = ["derive"] } diff --git a/duper-js-node/CHANGELOG.md b/duper-js-node/CHANGELOG.md index 05b528e..b7621a4 100644 --- a/duper-js-node/CHANGELOG.md +++ b/duper-js-node/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.1.1 (2025-12-07) ### Changed diff --git a/duper-js-node/Cargo.toml b/duper-js-node/Cargo.toml index 7367654..32101e1 100644 --- a/duper-js-node/Cargo.toml +++ b/duper-js-node/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1" -duper = { path = "../duper", version = "0.5", features = ["serde"] } +duper = { path = "../duper", version = "0.6", features = ["serde"] } napi = { version = "3", features = ["anyhow", "napi6"] } napi-derive = "3" serde_core = "1" diff --git a/duper-js-node/package.json b/duper-js-node/package.json index 103468a..0dd5bd0 100644 --- a/duper-js-node/package.json +++ b/duper-js-node/package.json @@ -1,6 +1,6 @@ { "name": "@duper-js/node", - "version": "0.1.1", + "version": "0.1.2", "description": "Node.JS bindings for Duper, the format that's super!", "keywords": [ "Duper", diff --git a/duper-js-wasm/CHANGELOG.md b/duper-js-wasm/CHANGELOG.md index a73207e..f59e056 100644 --- a/duper-js-wasm/CHANGELOG.md +++ b/duper-js-wasm/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.4.2 (2025-12-07) ### Changed diff --git a/duper-js-wasm/package.json b/duper-js-wasm/package.json index 67bce5d..80d32aa 100644 --- a/duper-js-wasm/package.json +++ b/duper-js-wasm/package.json @@ -1,6 +1,6 @@ { "name": "@duper-js/wasm", - "version": "0.4.2", + "version": "0.4.3", "description": "WebAssembly bindings for Duper, the format that's super!", "keywords": [ "Duper", diff --git a/duper-python/CHANGELOG.md b/duper-python/CHANGELOG.md index 603eb2b..6095b87 100644 --- a/duper-python/CHANGELOG.md +++ b/duper-python/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.4.2 (2025-12-07) ### Changed diff --git a/duper-python/Cargo.toml b/duper-python/Cargo.toml index 2feb132..85e843c 100644 --- a/duper-python/Cargo.toml +++ b/duper-python/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper-python" description = "The format that's super!" -version = "0.4.2" +version = "0.4.3" edition = "2024" rust-version = "1.88" license = "MIT" diff --git a/duper-python/src/de.rs b/duper-python/src/de.rs index fef7d13..5f03e7f 100644 --- a/duper-python/src/de.rs +++ b/duper-python/src/de.rs @@ -1,4 +1,4 @@ -use duper::{DuperTemporal, visitor::DuperVisitor}; +use duper::{DuperFloat, DuperTemporal, visitor::DuperVisitor}; use pyo3::{IntoPyObjectExt, prelude::*, types::*}; use crate::{Duper, temporal::TemporalString}; @@ -150,10 +150,10 @@ impl<'py> DuperVisitor for Visitor<'py> { fn visit_float<'a>( &mut self, identifier: Option<&duper::DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value { Ok(VisitorValue { - value: PyFloat::new(self.py, float).into_any(), + value: PyFloat::new(self.py, float.into_inner()).into_any(), duper: identifier .map(|identifier| Duper::from_identifier(identifier)?.into_pyobject(self.py)) .transpose()?, diff --git a/duper-python/src/ser/mod.rs b/duper-python/src/ser/mod.rs index bf58b81..e994fa0 100644 --- a/duper-python/src/ser/mod.rs +++ b/duper-python/src/ser/mod.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use duper::{DuperIdentifier, DuperKey, DuperObject, DuperValue}; +use duper::{DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue}; use pyo3::{BoundObject, exceptions::PyValueError, prelude::*, types::*}; use well_known_type::WellKnownType; @@ -79,7 +79,9 @@ pub(crate) fn serialize_pyany<'py>(obj: Bound<'py, PyAny>) -> PyResult() { Ok(DuperValue::Float { identifier: None, - inner: obj.extract()?, + inner: DuperFloat::try_new(obj.extract::()?).map_err(|error| { + PyErr::new::(format!("Invalid float: {error:?}")) + })?, }) } else if obj.is_none() { Ok(DuperValue::Null { identifier: None }) diff --git a/duper-vs-code/CHANGELOG.md b/duper-vs-code/CHANGELOG.md index 4938d4b..a5cf323 100644 --- a/duper-vs-code/CHANGELOG.md +++ b/duper-vs-code/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper_lsp` to 0.1.3. + ## 0.5.2 (2025-12-07) - Bump `duper_lsp` to 0.1.2. diff --git a/duper-vs-code/package.json b/duper-vs-code/package.json index ecade4b..adf9b7a 100644 --- a/duper-vs-code/package.json +++ b/duper-vs-code/package.json @@ -2,7 +2,7 @@ "name": "duper-vs-code", "displayName": "Duper", "description": "Duper support for Visual Studio Code", - "version": "0.5.2", + "version": "0.5.3", "publisher": "epic-eric", "homepage": "https://duper.dev.br", "bugs": { diff --git a/duper/CHANGELOG.md b/duper/CHANGELOG.md index 1bc2dd8..680a6cf 100644 --- a/duper/CHANGELOG.md +++ b/duper/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- **Breaking:** Force real values for floats. + ## 0.5.0 (2025-12-07) ### Added diff --git a/duper/Cargo.toml b/duper/Cargo.toml index f14ed36..b426028 100644 --- a/duper/Cargo.toml +++ b/duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper" description = "The format that's super!" -version = "0.5.0" +version = "0.6.0" edition = "2024" rust-version = "1.88" license = "MIT" @@ -21,6 +21,7 @@ serde = ["dep:serde_core"] ariadne = { version = "0.6", features = ["auto-color"] } base64 = "0.22" chumsky = "0.11" +decorum = "0.4.0" either = "1" indexmap = "2.12.1" owo-colors = { version = "4", optional = true } diff --git a/duper/src/ast.rs b/duper/src/ast.rs index 5e7c6f7..17d47df 100644 --- a/duper/src/ast.rs +++ b/duper/src/ast.rs @@ -14,6 +14,8 @@ use crate::{ visitor::DuperVisitor, }; +pub type DuperFloat = decorum::R64; + /// A Duper identifier: `MyIdentifier(...)` #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct DuperIdentifier<'a>(pub(crate) Cow<'a, str>); @@ -70,7 +72,7 @@ pub enum DuperValue<'a> { /// The identifier of this value. identifier: Option>, /// The actual value of the float. - inner: f64, + inner: DuperFloat, }, /// A boolean. Boolean { diff --git a/duper/src/format.rs b/duper/src/format.rs index 50511bd..a90ef2e 100644 --- a/duper/src/format.rs +++ b/duper/src/format.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use base64::{Engine, prelude::BASE64_STANDARD}; use crate::{ + DuperFloat, ast::DuperKey, escape::{escape_bytes, escape_str, is_invisible_unicode}, }; @@ -181,27 +182,27 @@ pub fn format_duper_bytes(bytes: &[u8]) -> String { } } -// Format a Temporal value for Duper. +/// Format a Temporal value for Duper. pub fn format_temporal(temporal: impl AsRef) -> String { format!("'{}'", temporal.as_ref().trim()) } -// Format an integer for Duper. +/// Format an integer for Duper. pub fn format_integer(integer: i64) -> String { integer.to_string() } -// Format a float for Duper. -pub fn format_float(float: f64) -> String { - ryu::Buffer::new().format(float).into() +/// Format a float for Duper. +pub fn format_float(float: DuperFloat) -> String { + ryu::Buffer::new().format(float.into_inner()).into() } -// Format a boolean for Duper. +/// Format a boolean for Duper. pub fn format_boolean(bool: bool) -> &'static str { if bool { "true" } else { "false" } } -// Format a null value for Duper. +/// Format a null value for Duper. pub fn format_null() -> &'static str { "null" } diff --git a/duper/src/lib.rs b/duper/src/lib.rs index 477aab8..a1f71cb 100644 --- a/duper/src/lib.rs +++ b/duper/src/lib.rs @@ -35,6 +35,8 @@ //! for use with [`axum`](https://docs.rs/axum). //! +pub use decorum::constraint::NotRealError; + pub mod ast; pub mod escape; pub mod format; @@ -45,8 +47,8 @@ pub mod validate; pub mod visitor; pub use ast::{ - DuperIdentifier, DuperIdentifierTryFromError, DuperKey, DuperObject, DuperObjectTryFromError, - DuperTemporal, DuperTemporalDuration, DuperTemporalIdentifier, + DuperFloat, DuperIdentifier, DuperIdentifierTryFromError, DuperKey, DuperObject, + DuperObjectTryFromError, DuperTemporal, DuperTemporalDuration, DuperTemporalIdentifier, DuperTemporalIdentifierTryFromError, DuperTemporalInstant, DuperTemporalPlainDate, DuperTemporalPlainDateTime, DuperTemporalPlainMonthDay, DuperTemporalPlainTime, DuperTemporalPlainYearMonth, DuperTemporalTryFromError, DuperTemporalUnspecified, diff --git a/duper/src/parser/mod.rs b/duper/src/parser/mod.rs index 21e7373..9723d96 100644 --- a/duper/src/parser/mod.rs +++ b/duper/src/parser/mod.rs @@ -8,6 +8,7 @@ use chumsky::prelude::*; pub(crate) mod temporal; use crate::{ + DuperFloat, ast::{DuperIdentifier, DuperKey, DuperObject, DuperValue}, escape::{unescape_bytes, unescape_str}, parser::temporal::{temporal_specified, temporal_unspecified}, @@ -198,9 +199,9 @@ pub fn identified_value<'a>() identifier: None, inner: Cow::Borrowed(str), }), - float().map(|inner| DuperValue::Float { + float().map(|float| DuperValue::Float { identifier: None, - inner, + inner: DuperFloat::assert(float), }), integer().map(|inner| DuperValue::Integer { identifier: None, @@ -650,8 +651,8 @@ pub(crate) fn control_character<'a>() #[cfg(test)] mod duper_parser_tests { use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperParser, DuperTemporal, DuperTemporalInstant, - DuperValue, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperParser, DuperTemporal, + DuperTemporalInstant, DuperValue, }; #[test] @@ -1446,15 +1447,15 @@ mod duper_parser_tests { inner: vec![ DuperValue::Float { identifier: None, - inner: 18.5 + inner: DuperFloat::assert(18.5) }, DuperValue::Float { identifier: None, - inner: 15.2 + inner: DuperFloat::assert(15.2) }, DuperValue::Float { identifier: None, - inner: 7.8 + inner: DuperFloat::assert(7.8) }, ], } @@ -1463,7 +1464,7 @@ mod duper_parser_tests { DuperKey(Cow::Borrowed("weight")), DuperValue::Float { identifier: Some(DuperIdentifier(Cow::Borrowed("Weight"))), - inner: 0.285 + inner: DuperFloat::assert(0.285) } ), ( @@ -1570,7 +1571,7 @@ mod duper_parser_tests { DuperKey(Cow::Borrowed("average")), DuperValue::Float { identifier: None, - inner: 4.5, + inner: DuperFloat::assert(4.5), } ), ( diff --git a/duper/src/serde/de.rs b/duper/src/serde/de.rs index 1cdb546..6e86e32 100644 --- a/duper/src/serde/de.rs +++ b/duper/src/serde/de.rs @@ -179,7 +179,7 @@ impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> { .deserialize_any(visitor) } Some(DuperValue::Integer { inner: integer, .. }) => visitor.visit_i64(integer), - Some(DuperValue::Float { inner: float, .. }) => visitor.visit_f64(float), + Some(DuperValue::Float { inner: float, .. }) => visitor.visit_f64(float.into_inner()), Some(DuperValue::Boolean { inner: boolean, .. }) => visitor.visit_bool(boolean), Some(DuperValue::Null { .. }) => visitor.visit_none(), None => Err(de::Error::custom("already consumed deserializer value")), diff --git a/duper/src/serde/meta.rs b/duper/src/serde/meta.rs index d630f90..1bebf83 100644 --- a/duper/src/serde/meta.rs +++ b/duper/src/serde/meta.rs @@ -12,7 +12,7 @@ use serde_core::{ }; use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue, serde::error::DuperSerdeError, }; @@ -271,7 +271,7 @@ enum DeDuperInner<'b> { Bytes(Cow<'b, [u8]>), Temporal(DeDuperTemporal<'b>), Integer(i64), - Float(f64), + Float(DuperFloat), Boolean(bool), Null, } @@ -637,7 +637,7 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { } else if let float = v as f64 && float as i128 == v { - Ok(DeDuperInner::Float(float)) + Ok(DeDuperInner::Float(DuperFloat::assert(float))) } else { Ok(DeDuperInner::String(Cow::Owned(v.to_string()))) } @@ -673,7 +673,7 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { } else if let float = v as f64 && float as u64 == v { - Ok(DeDuperInner::Float(float)) + Ok(DeDuperInner::Float(DuperFloat::assert(float))) } else { Ok(DeDuperInner::String(Cow::Owned(v.to_string()))) } @@ -688,7 +688,7 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { } else if let float = v as f64 && float as u128 == v { - Ok(DeDuperInner::Float(float)) + Ok(DeDuperInner::Float(DuperFloat::assert(float))) } else { Ok(DeDuperInner::String(Cow::Owned(v.to_string()))) } @@ -698,7 +698,9 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { where E: Error, { - Ok(DeDuperInner::Float(v)) + Ok(DeDuperInner::Float(DuperFloat::try_new(v).map_err( + |error| serde_core::de::Error::custom(format!("invalid float: {error:?}",)), + )?)) } fn visit_str(self, v: &str) -> Result @@ -775,10 +777,7 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { where A: SeqAccess<'de>, { - let mut vec = seq - .size_hint() - .map(|len| Vec::with_capacity(len)) - .unwrap_or_default(); + let mut vec = seq.size_hint().map(Vec::with_capacity).unwrap_or_default(); while let Some(element) = seq.next_element()? { vec.push(element); } @@ -789,10 +788,7 @@ impl<'de> Visitor<'de> for DeDuperInnerVisitor { where A: MapAccess<'de>, { - let mut vec = map - .size_hint() - .map(|len| Vec::with_capacity(len)) - .unwrap_or_default(); + let mut vec = map.size_hint().map(Vec::with_capacity).unwrap_or_default(); while let Some(element) = map.next_entry()? { vec.push(element); } @@ -1126,7 +1122,7 @@ mod serde_meta_tests { use insta::assert_snapshot; use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperValue, PrettyPrinter, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue, PrettyPrinter, serde::{de::Deserializer, ser::Serializer}, }; @@ -1295,7 +1291,7 @@ mod serde_meta_tests { DuperKey::from("float"), DuperValue::Float { identifier: None, - inner: 8.25, + inner: DuperFloat::assert(8.25), }, ), ( @@ -1352,7 +1348,7 @@ mod serde_meta_tests { identifier: Some( DuperIdentifier::try_from("MyFloat").expect("valid identifier"), ), - inner: 8.25, + inner: DuperFloat::assert(8.25), }, DuperValue::Boolean { identifier: Some( diff --git a/duper/src/serde/mod.rs b/duper/src/serde/mod.rs index 2e2d913..4b32ba0 100644 --- a/duper/src/serde/mod.rs +++ b/duper/src/serde/mod.rs @@ -22,7 +22,8 @@ use serde_core::{ }; use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperTemporalIdentifier, DuperValue, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperTemporalIdentifier, + DuperValue, }; impl<'a> Serialize for DuperIdentifier<'a> { @@ -102,7 +103,7 @@ impl<'a> Serialize for DuperValue<'a> { } }, DuperValue::Integer { inner, .. } => serializer.serialize_i64(*inner), - DuperValue::Float { inner, .. } => serializer.serialize_f64(*inner), + DuperValue::Float { inner, .. } => serializer.serialize_f64(*inner.as_ref()), DuperValue::Boolean { inner, .. } => serializer.serialize_bool(*inner), DuperValue::Null { .. } => serializer.serialize_none(), } @@ -169,7 +170,7 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { DuperIdentifier::try_from(Cow::Borrowed("I128")) .expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -220,7 +221,7 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { DuperIdentifier::try_from(Cow::Borrowed("U64")) .expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -250,7 +251,7 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { DuperIdentifier::try_from(Cow::Borrowed("U128")) .expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -269,7 +270,9 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { { Ok(DuperValue::Float { identifier: None, - inner: v, + inner: DuperFloat::try_new(v).map_err(|error| { + serde_core::de::Error::custom(format!("invalid float: {error:?}",)) + })?, }) } @@ -362,10 +365,7 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { where A: SeqAccess<'de>, { - let mut vec = seq - .size_hint() - .map(|len| Vec::with_capacity(len)) - .unwrap_or_default(); + let mut vec = seq.size_hint().map(Vec::with_capacity).unwrap_or_default(); while let Some(element) = seq.next_element()? { vec.push(element); } @@ -379,10 +379,7 @@ impl<'de> Deserialize<'de> for DuperValue<'de> { where A: MapAccess<'de>, { - let mut vec = map - .size_hint() - .map(|len| Vec::with_capacity(len)) - .unwrap_or_default(); + let mut vec = map.size_hint().map(Vec::with_capacity).unwrap_or_default(); while let Some(element) = map.next_entry()? { vec.push(element); } @@ -486,7 +483,7 @@ mod serde_tests { use serde::{Deserialize, Serialize}; use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperValue, PrettyPrinter, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue, PrettyPrinter, serde::{de::Deserializer, ser::Serializer}, }; @@ -623,7 +620,7 @@ mod serde_tests { DuperKey::from("float"), DuperValue::Float { identifier: None, - inner: 8.25, + inner: DuperFloat::assert(8.25), }, ), ( @@ -680,7 +677,7 @@ mod serde_tests { DuperKey::from("float"), DuperValue::Float { identifier: None, - inner: 8.25, + inner: DuperFloat::assert(8.25), }, ), ( @@ -727,7 +724,7 @@ mod serde_tests { identifier: Some( DuperIdentifier::try_from("MyFloat").expect("valid identifier"), ), - inner: 8.25, + inner: DuperFloat::assert(8.25), }, DuperValue::Boolean { identifier: Some( @@ -768,7 +765,7 @@ mod serde_tests { }, DuperValue::Float { identifier: None, - inner: 8.25, + inner: DuperFloat::assert(8.25), }, DuperValue::Boolean { identifier: None, diff --git a/duper/src/serde/ser.rs b/duper/src/serde/ser.rs index 6934e7a..d76ef9e 100644 --- a/duper/src/serde/ser.rs +++ b/duper/src/serde/ser.rs @@ -3,8 +3,8 @@ use std::{borrow::Cow, marker::PhantomData}; use crate::{ - DuperIdentifier, DuperKey, DuperObject, DuperValue, PrettyPrinter as DuperPrettyPrinter, - Serializer as DuperSerializer, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue, + PrettyPrinter as DuperPrettyPrinter, Serializer as DuperSerializer, }; use serde_core::{Serialize, ser}; @@ -155,7 +155,7 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { identifier: Some( DuperIdentifier::try_from(Cow::Borrowed("I128")).expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -201,7 +201,7 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { identifier: Some( DuperIdentifier::try_from(Cow::Borrowed("U64")).expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -226,7 +226,7 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { identifier: Some( DuperIdentifier::try_from(Cow::Borrowed("U128")).expect("valid identifier"), ), - inner: float, + inner: DuperFloat::assert(float), }) } else { Ok(DuperValue::String { @@ -239,16 +239,15 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { } fn serialize_f32(self, v: f32) -> Result { - Ok(DuperValue::Float { - identifier: None, - inner: v.into(), - }) + self.serialize_f64(v.into()) } fn serialize_f64(self, v: f64) -> Result { Ok(DuperValue::Float { identifier: None, - inner: v, + inner: DuperFloat::try_new(v).map_err(|error| { + DuperSerdeError::invalid_value(format!("invalid float: {error:?}")) + })?, }) } @@ -349,7 +348,7 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { fn serialize_seq(self, len: Option) -> Result { Ok(Self::SerializeSeq { serializer: self, - elements: len.map(|len| Vec::with_capacity(len)).unwrap_or_default(), + elements: len.map(Vec::with_capacity).unwrap_or_default(), }) } @@ -391,7 +390,7 @@ impl<'ser, 'a> ser::Serializer for &'ser mut Serializer<'a> { Ok(Self::SerializeMap { serializer: self, identifier: None, - entries: len.map(|len| Vec::with_capacity(len)).unwrap_or_default(), + entries: len.map(Vec::with_capacity).unwrap_or_default(), next_key: None, }) } diff --git a/duper/src/visitor/ansi.rs b/duper/src/visitor/ansi.rs index ba14764..d747362 100644 --- a/duper/src/visitor/ansi.rs +++ b/duper/src/visitor/ansi.rs @@ -3,6 +3,7 @@ use std::io::{Error, Write}; use crate::{ + DuperFloat, ast::{DuperIdentifier, DuperObject, DuperTemporal, DuperValue}, format::{ format_boolean, format_duper_bytes, format_duper_string, format_float, format_integer, @@ -397,7 +398,11 @@ impl<'ansi> DuperVisitor for Ansi<'ansi> { Ok(()) } - fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { + fn visit_float( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + float: DuperFloat, + ) -> Self::Value { if !self.strip_identifiers && let Some(identifier) = identifier { @@ -483,7 +488,7 @@ mod ansi_tests { use insta::assert_debug_snapshot; use super::{ANSI_THEME, Ansi, VSCODE_DARK_PLUS_THEME}; - use crate::{DuperIdentifier, DuperKey, DuperObject, DuperValue}; + use crate::{DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue}; fn example_value() -> DuperValue<'static> { DuperValue::Object { @@ -524,15 +529,15 @@ mod ansi_tests { inner: vec![ DuperValue::Float { identifier: None, - inner: 18.5, + inner: DuperFloat::assert(18.5), }, DuperValue::Float { identifier: None, - inner: 15.2, + inner: DuperFloat::assert(15.2), }, DuperValue::Float { identifier: None, - inner: 7.8, + inner: DuperFloat::assert(7.8), }, ], }, @@ -541,7 +546,7 @@ mod ansi_tests { DuperKey(Cow::Borrowed("weight")), DuperValue::Float { identifier: Some(DuperIdentifier(Cow::Borrowed("Weight"))), - inner: 0.285, + inner: DuperFloat::assert(0.285), }, ), ( @@ -646,7 +651,7 @@ mod ansi_tests { DuperKey(Cow::Borrowed("average")), DuperValue::Float { identifier: None, - inner: 4.5, + inner: DuperFloat::assert(4.5), }, ), ( diff --git a/duper/src/visitor/mod.rs b/duper/src/visitor/mod.rs index 7855070..a5a2a66 100644 --- a/duper/src/visitor/mod.rs +++ b/duper/src/visitor/mod.rs @@ -5,7 +5,10 @@ pub mod ansi; pub mod pretty_printer; pub mod serializer; -use crate::{DuperIdentifier, DuperTemporal, DuperValue, ast::DuperObject}; +use crate::{ + DuperIdentifier, DuperTemporal, DuperValue, + ast::{DuperFloat, DuperObject}, +}; /// A trait for implementing a Duper visitor. You can visit a `DuperValue` /// with `value.accept(&mut visitor)`. @@ -17,6 +20,7 @@ use crate::{DuperIdentifier, DuperTemporal, DuperValue, ast::DuperObject}; /// DuperIdentifier, DuperObject, DuperTemporal, /// DuperValue, visitor::DuperVisitor, /// }; +/// # use duper::DuperFloat; /// /// struct MyVisitor; /// @@ -54,42 +58,35 @@ use crate::{DuperIdentifier, DuperTemporal, DuperValue, ast::DuperObject}; /// # identifier: Option<&DuperIdentifier<'a>>, /// # tuple: &[DuperValue<'a>], /// # ) -> Self::Value {} -/// # /// # fn visit_string<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>>, /// # string: &'a str, /// # ) -> Self::Value {} -/// # /// # fn visit_bytes<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>>, /// # bytes: &'a [u8], /// # ) -> Self::Value {} -/// # /// # fn visit_temporal<'a>( /// # &mut self, /// # temporal: &DuperTemporal<'a>, /// # ) -> Self::Value {} -/// # /// # fn visit_integer<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>>, /// # integer: i64, /// # ) -> Self::Value {} -/// # /// # fn visit_float<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>>, -/// # float: f64, +/// # float: DuperFloat, /// # ) -> Self::Value {} -/// # /// # fn visit_boolean<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>>, /// # boolean: bool, /// # ) -> Self::Value {} -/// # /// # fn visit_null<'a>( /// # &mut self, /// # identifier: Option<&DuperIdentifier<'a>> @@ -149,7 +146,7 @@ pub trait DuperVisitor { fn visit_float<'a>( &mut self, identifier: Option<&DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value; /// Visits a boolean. diff --git a/duper/src/visitor/pretty_printer.rs b/duper/src/visitor/pretty_printer.rs index 8bd19f7..0bfb8e1 100644 --- a/duper/src/visitor/pretty_printer.rs +++ b/duper/src/visitor/pretty_printer.rs @@ -1,6 +1,7 @@ //! Utilities for pretty-printing Duper values. use crate::{ + DuperFloat, ast::{DuperIdentifier, DuperObject, DuperTemporal, DuperValue}, format::{ format_boolean, format_duper_bytes, format_duper_string, format_float, format_integer, @@ -287,7 +288,11 @@ impl<'pp> DuperVisitor for PrettyPrinter<'pp> { } } - fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { + fn visit_float( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + float: DuperFloat, + ) -> Self::Value { if !self.strip_identifiers && let Some(identifier) = identifier { @@ -333,7 +338,9 @@ mod pretty_printer_tests { use insta::assert_snapshot; use super::PrettyPrinter; - use crate::{DuperIdentifier, DuperKey, DuperObject, DuperValue, parser::DuperParser}; + use crate::{ + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue, parser::DuperParser, + }; #[test] fn empty_object() { @@ -608,7 +615,7 @@ mod pretty_printer_tests { DuperIdentifier::try_from(Cow::Borrowed("Float")) .expect("valid identifier"), ), - inner: 4.2, + inner: DuperFloat::assert(4.2), }, DuperValue::Boolean { identifier: Some( diff --git a/duper/src/visitor/serializer.rs b/duper/src/visitor/serializer.rs index c82e4c5..acdfbf7 100644 --- a/duper/src/visitor/serializer.rs +++ b/duper/src/visitor/serializer.rs @@ -1,7 +1,7 @@ //! Utilities for serializing Duper values. use crate::{ - DuperTemporal, + DuperFloat, DuperTemporal, ast::{DuperIdentifier, DuperObject, DuperValue}, format::{ format_boolean, format_duper_bytes, format_duper_string, format_float, format_integer, @@ -225,7 +225,11 @@ impl DuperVisitor for Serializer { } } - fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { + fn visit_float( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + float: DuperFloat, + ) -> Self::Value { if !self.strip_identifiers && let Some(identifier) = identifier { @@ -270,7 +274,7 @@ mod serializer_tests { use insta::assert_snapshot; use super::Serializer; - use crate::{DuperIdentifier, DuperKey, DuperObject, DuperParser, DuperValue}; + use crate::{DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperParser, DuperValue}; fn example_value() -> DuperValue<'static> { DuperValue::Object { @@ -311,15 +315,15 @@ mod serializer_tests { inner: vec![ DuperValue::Float { identifier: None, - inner: 18.5, + inner: DuperFloat::assert(18.5), }, DuperValue::Float { identifier: None, - inner: 15.2, + inner: DuperFloat::assert(15.2), }, DuperValue::Float { identifier: None, - inner: 7.8, + inner: DuperFloat::assert(7.8), }, ], }, @@ -328,7 +332,7 @@ mod serializer_tests { DuperKey(Cow::Borrowed("weight")), DuperValue::Float { identifier: Some(DuperIdentifier(Cow::Borrowed("Weight"))), - inner: 0.285, + inner: DuperFloat::assert(0.285), }, ), ( @@ -433,7 +437,7 @@ mod serializer_tests { DuperKey(Cow::Borrowed("average")), DuperValue::Float { identifier: None, - inner: 4.5, + inner: DuperFloat::assert(4.5), }, ), ( diff --git a/duper_lsp/CHANGELOG.md b/duper_lsp/CHANGELOG.md index 281817f..6cef36e 100644 --- a/duper_lsp/CHANGELOG.md +++ b/duper_lsp/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.1.3 (2025-12-07) ### Added diff --git a/duper_lsp/Cargo.toml b/duper_lsp/Cargo.toml index 7c5675e..127b922 100644 --- a/duper_lsp/Cargo.toml +++ b/duper_lsp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper_lsp" description = "LSP implementation for Duper." -version = "0.1.3" +version = "0.1.4" edition = "2024" rust-version = "1.88" license = "MIT" @@ -16,7 +16,7 @@ keywords = ["duper", "lsp", "formatting"] async-lsp = { version = "0.2.2", features = ["tokio"] } base64 = "0.22.1" clap = { version = "4.5.53", features = ["derive"] } -duper = { version = "0.5", path = "../duper" } +duper = { version = "0.6", path = "../duper" } duperfmt = { version = "0.1", path = "../duperfmt" } futures = "0.3.31" ipnet = "2.11.0" diff --git a/duper_lsp/src/diagnostics.rs b/duper_lsp/src/diagnostics.rs index 7520ba4..5d13232 100644 --- a/duper_lsp/src/diagnostics.rs +++ b/duper_lsp/src/diagnostics.rs @@ -990,8 +990,8 @@ pub(crate) fn get_diagnostics(source: &str, tree: &Tree, is_utf8: bool) -> Vec Vec EpicEric.Duper - 0.0.6 + 0.0.7 Duper Eric Rodrigues Pires <eric@eric.dev.br> MIT diff --git a/duper_uniffi/src/duper.udl b/duper_uniffi/src/duper.udl index 18e5790..59becef 100644 --- a/duper_uniffi/src/duper.udl +++ b/duper_uniffi/src/duper.udl @@ -24,6 +24,7 @@ enum DuperError { "InvalidIdentifier", "InvalidObject", "InvalidTemporal", + "InvalidFloat", }; dictionary SerializeOptions { diff --git a/duper_uniffi/src/lib.rs b/duper_uniffi/src/lib.rs index d913ef6..9729bd5 100644 --- a/duper_uniffi/src/lib.rs +++ b/duper_uniffi/src/lib.rs @@ -67,6 +67,8 @@ pub enum DuperError { InvalidObject(#[from] DuperObjectTryFromError<'static>), #[error("Temporal error: {0}")] InvalidTemporal(#[from] DuperTemporalTryFromError<'static>), + #[error("Invalid float: {0}")] + InvalidFloat(#[from] duper::NotRealError), } pub fn parse(input: &str, parse_any: bool) -> Result { diff --git a/duper_uniffi/src/parse.rs b/duper_uniffi/src/parse.rs index 4b32908..557bb0f 100644 --- a/duper_uniffi/src/parse.rs +++ b/duper_uniffi/src/parse.rs @@ -1,4 +1,4 @@ -use duper::{DuperIdentifier, DuperObject, DuperTemporal, visitor::DuperVisitor}; +use duper::{DuperFloat, DuperIdentifier, DuperObject, DuperTemporal, visitor::DuperVisitor}; use crate::{DuperObjectEntry, DuperValue}; @@ -100,11 +100,11 @@ impl DuperVisitor for UniffiVisitor { fn visit_float<'a>( &mut self, identifier: Option<&DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value { DuperValue::Float { identifier: identifier.map(|identifier| identifier.as_ref().to_string()), - value: float, + value: float.into_inner(), } } diff --git a/duper_uniffi/src/serialize.rs b/duper_uniffi/src/serialize.rs index 47ce121..66b1d88 100644 --- a/duper_uniffi/src/serialize.rs +++ b/duper_uniffi/src/serialize.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use duper::{DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue}; +use duper::{DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue}; use crate::{DuperError, DuperObjectEntry, DuperValue as Value}; @@ -74,7 +74,7 @@ impl Value { }), Value::Float { identifier, value } => Ok(DuperValue::Float { identifier: identifier.map(DuperIdentifier::try_from).transpose()?, - inner: value, + inner: DuperFloat::try_new(value)?, }), Value::Boolean { identifier, value } => Ok(DuperValue::Boolean { identifier: identifier.map(DuperIdentifier::try_from).transpose()?, diff --git a/duper_website/src/visitor/saphyr.rs b/duper_website/src/visitor/saphyr.rs index 35a4816..0d4ed53 100644 --- a/duper_website/src/visitor/saphyr.rs +++ b/duper_website/src/visitor/saphyr.rs @@ -1,5 +1,7 @@ use base64::{Engine, prelude::BASE64_STANDARD}; -use duper::{DuperIdentifier, DuperObject, DuperTemporal, DuperValue, visitor::DuperVisitor}; +use duper::{ + DuperFloat, DuperIdentifier, DuperObject, DuperTemporal, DuperValue, visitor::DuperVisitor, +}; use saphyr::{ScalarOwned, ScalarStyle, Tag, YamlOwned}; // A visitor that serializes Duper into a Saphyr YAML value. @@ -87,9 +89,11 @@ impl DuperVisitor for SaphyrVisitor { fn visit_float<'a>( &mut self, _identifier: Option<&DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value { - Ok(YamlOwned::Value(ScalarOwned::FloatingPoint(float.into()))) + Ok(YamlOwned::Value(ScalarOwned::FloatingPoint( + float.into_inner().into(), + ))) } fn visit_boolean<'a>( diff --git a/duper_website/src/visitor/serde.rs b/duper_website/src/visitor/serde.rs index b16651d..ecf7f1c 100644 --- a/duper_website/src/visitor/serde.rs +++ b/duper_website/src/visitor/serde.rs @@ -2,7 +2,8 @@ use std::borrow::Cow; use base64::{Engine, prelude::BASE64_STANDARD}; use duper::{ - DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue, visitor::DuperVisitor, + DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperTemporal, DuperValue, + visitor::DuperVisitor, }; // A visitor that simplifies Duper values for Serde serializers. @@ -101,7 +102,7 @@ impl DuperVisitor for SerdeVisitor { fn visit_float<'a>( &mut self, identifier: Option<&DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value { DuperValue::Float { identifier: identifier.map(|identifier| identifier.static_clone()), diff --git a/duper_website/src/visitor/toml.rs b/duper_website/src/visitor/toml.rs index b297ede..fac74be 100644 --- a/duper_website/src/visitor/toml.rs +++ b/duper_website/src/visitor/toml.rs @@ -1,7 +1,9 @@ use std::str::FromStr; use base64::{Engine, prelude::BASE64_STANDARD}; -use duper::{DuperIdentifier, DuperObject, DuperTemporal, DuperValue, visitor::DuperVisitor}; +use duper::{ + DuperFloat, DuperIdentifier, DuperObject, DuperTemporal, DuperValue, visitor::DuperVisitor, +}; use toml::{Value, value::Datetime}; use crate::temporal::{Instant, PlainDate, PlainDateTime, PlainTime}; @@ -124,9 +126,9 @@ impl DuperVisitor for TomlVisitor { fn visit_float<'a>( &mut self, _identifier: Option<&DuperIdentifier<'a>>, - float: f64, + float: DuperFloat, ) -> Self::Value { - Ok(Some(Value::Float(float))) + Ok(Some(Value::Float(float.into_inner()))) } fn visit_boolean<'a>( diff --git a/duperfmt/src/main.rs b/duperfmt/src/main.rs index c908074..e114339 100644 --- a/duperfmt/src/main.rs +++ b/duperfmt/src/main.rs @@ -41,9 +41,9 @@ struct Cli { #[derive(Error, Debug, Diagnostic)] struct TreeSitterReport { #[source_code] - src: NamedSource, + _src: NamedSource, #[label(collection)] - reports: Vec, + _reports: Vec, } impl Display for TreeSitterReport { @@ -129,8 +129,8 @@ fn main() -> miette::Result<()> { } if !reports.is_empty() { return Err(TreeSitterReport { - src: NamedSource::new(src, input), - reports, + _src: NamedSource::new(src, input), + _reports: reports, } .into()); } diff --git a/duperq/CHANGELOG.md b/duperq/CHANGELOG.md index 64977dc..919c9e7 100644 --- a/duperq/CHANGELOG.md +++ b/duperq/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.1.1 (2025-12-07) ### Changed diff --git a/duperq/Cargo.toml b/duperq/Cargo.toml index fa755c0..b436ab7 100644 --- a/duperq/Cargo.toml +++ b/duperq/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duperq" description = "LSP implementation for Duper." -version = "0.1.1" +version = "0.1.2" edition = "2024" rust-version = "1.88" license = "MIT" @@ -14,10 +14,11 @@ keywords = ["duper", "filter"] [dependencies] anyhow = "1.0.100" +approx = "0.5.1" async-trait = "0.1.89" chumsky = "0.11.2" clap = { version = "4.5.53", features = ["derive"] } -duper = { version = "0.5", path = "../duper", features = ["ansi"] } +duper = { version = "0.6", path = "../duper", features = ["ansi"] } futures = "0.3.31" num_cpus = "1.17.0" regex = "1.12.2" diff --git a/duperq/src/filter.rs b/duperq/src/filter.rs index cf1d66c..03e75d8 100644 --- a/duperq/src/filter.rs +++ b/duperq/src/filter.rs @@ -1,6 +1,7 @@ use std::{cmp::Ordering, fmt::Display, str::FromStr}; -use duper::{DuperTemporal, DuperValue}; +use approx::{abs_diff_eq, abs_diff_ne}; +use duper::{DuperFloat, DuperTemporal, DuperValue}; use temporal_rs::{ Duration, Instant, PlainDate, PlainDateTime, PlainMonthDay, PlainTime, PlainYearMonth, TemporalError, ZonedDateTime, @@ -148,7 +149,7 @@ pub(crate) enum EqValue { TemporalPlainMonthDay(PlainMonthDay), TemporalDuration(Duration), Integer(i64), - Float(f64, Option), + Float(DuperFloat, Option), Boolean(bool), Null, } @@ -156,7 +157,7 @@ pub(crate) enum EqValue { impl EqValue { pub(crate) fn try_from_duper( value: DuperValue<'_>, - epsilon: Option, + epsilon: Option, ) -> Result { match value { DuperValue::Object { .. } => Err(TryFromDuperValueError::InvalidType("Object")), @@ -276,13 +277,21 @@ impl DuperFilter for EqFilter { } (EqValue::Integer(this), DuperValue::Integer { inner: that, .. }) => this == that, (EqValue::Float(this, epsilon), DuperValue::Float { inner: that, .. }) => { - (this - that).abs() <= epsilon.unwrap_or(0.0).abs() + abs_diff_eq!( + this, + that, + epsilon = epsilon.unwrap_or(DuperFloat::assert(0.0)) + ) } (EqValue::Integer(this), DuperValue::Float { inner: that, .. }) => { - *this == *that as i64 + *this == *that.as_ref() as i64 } (EqValue::Float(this, epsilon), DuperValue::Integer { inner: that, .. }) => { - (this - *that as f64).abs() <= epsilon.unwrap_or(0.0).abs() + abs_diff_eq!( + this, + &DuperFloat::assert(*that as f64), + epsilon = epsilon.unwrap_or(DuperFloat::assert(0.0)) + ) } (EqValue::Boolean(this), DuperValue::Boolean { inner: that, .. }) => this == that, (EqValue::Null, DuperValue::Null { .. }) => true, @@ -370,13 +379,21 @@ impl DuperFilter for NeFilter { } (EqValue::Integer(this), DuperValue::Integer { inner: that, .. }) => this != that, (EqValue::Float(this, epsilon), DuperValue::Float { inner: that, .. }) => { - (this - that).abs() > epsilon.unwrap_or(0.0).abs() + abs_diff_ne!( + this, + that, + epsilon = epsilon.unwrap_or(DuperFloat::assert(0.0)) + ) } (EqValue::Integer(this), DuperValue::Float { inner: that, .. }) => { - *this != *that as i64 + *this != *that.as_ref() as i64 } (EqValue::Float(this, epsilon), DuperValue::Integer { inner: that, .. }) => { - (this - *that as f64).abs() > epsilon.unwrap_or(0.0).abs() + abs_diff_ne!( + this, + &DuperFloat::assert(*that as f64), + epsilon = epsilon.unwrap_or(DuperFloat::assert(0.0)) + ) } (EqValue::Boolean(this), DuperValue::Boolean { inner: that, .. }) => this != that, (EqValue::Null, DuperValue::Null { .. }) => false, @@ -395,7 +412,7 @@ pub(crate) enum CmpValue { TemporalPlainYearMonth(PlainYearMonth), TemporalDuration(Duration), Integer(i64), - Float(f64), + Float(DuperFloat), } impl TryFrom> for CmpValue { @@ -507,13 +524,13 @@ macro_rules! cmp_filter { matches!(that.cmp(this), $ord) } (CmpValue::Float(this), DuperValue::Float { inner: that, .. }) => { - matches!(that.partial_cmp(this), Some($ord)) + matches!(that.cmp(this), $ord) } (CmpValue::Integer(this), DuperValue::Float { inner: that, .. }) => { - matches!((*that as i64).cmp(this), $ord) + matches!((*that.as_ref() as i64).cmp(this), $ord) } (CmpValue::Float(this), DuperValue::Integer { inner: that, .. }) => { - matches!((*that as f64).partial_cmp(this), Some($ord)) + matches!(DuperFloat::assert(*that as f64).cmp(this), $ord) } _ => false, } diff --git a/duperq/src/formatter.rs b/duperq/src/formatter.rs index 6dfa62b..173adce 100644 --- a/duperq/src/formatter.rs +++ b/duperq/src/formatter.rs @@ -1,7 +1,7 @@ // duperq 'span.tagged && span[0]name == sp0001 | "[${level}] ${span[0]time} - ${span[0]status} ${telemetry.duration:ms}"' use duper::{ - DuperIdentifier, DuperObject, DuperTemporal, DuperValue, + DuperFloat, DuperIdentifier, DuperObject, DuperTemporal, DuperValue, format::{ format_boolean, format_duper_bytes, format_duper_string, format_float, format_integer, format_key, format_null, format_temporal, @@ -217,7 +217,11 @@ impl DuperVisitor for FormatterVisitor { } } - fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { + fn visit_float( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + float: DuperFloat, + ) -> Self::Value { if let Some(identifier) = identifier { let value = format_float(float); self.buf.push_str(&format!("{identifier}({value})")); diff --git a/duperq/src/types.rs b/duperq/src/types.rs index 58fad1b..35a4c12 100644 --- a/duperq/src/types.rs +++ b/duperq/src/types.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use duper::{DuperTemporal, DuperValue}; +use duper::{DuperFloat, DuperTemporal, DuperValue}; use crate::filter::{DuperFilter, IsTruthyFilter}; @@ -234,7 +234,7 @@ impl DuperType { }, ) => Some(DuperValue::Integer { identifier: identifier.clone(), - inner: *float as i64, + inner: *float.as_ref() as i64, }), ( DuperType::Float, @@ -244,7 +244,7 @@ impl DuperType { }, ) => Some(DuperValue::Float { identifier: identifier.clone(), - inner: *integer as f64, + inner: DuperFloat::try_new(*integer as f64).ok()?, }), (DuperType::Boolean, value) => Some(DuperValue::Boolean { identifier: value.identifier(), diff --git a/serde_duper/CHANGELOG.md b/serde_duper/CHANGELOG.md index 6d0641b..cba4db6 100644 --- a/serde_duper/CHANGELOG.md +++ b/serde_duper/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.4.2 (2025-12-07) ### Added diff --git a/serde_duper/Cargo.toml b/serde_duper/Cargo.toml index 1b45257..1c54d23 100644 --- a/serde_duper/Cargo.toml +++ b/serde_duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "serde_duper" description = "Serde support for Duper." -version = "0.4.2" +version = "0.4.3" edition = "2024" rust-version = "1.88" license = "MIT" @@ -29,7 +29,7 @@ uuid = ["dep:uuid"] [dependencies] # Core dependencies -duper = { path = "../duper", version = "0.5", features = ["serde"] } +duper = { path = "../duper", version = "0.6", features = ["serde"] } serde_bytes = "0.11" serde_core = "1" diff --git a/tracing_duper/CHANGELOG.md b/tracing_duper/CHANGELOG.md index 38a64af..12801ce 100644 --- a/tracing_duper/CHANGELOG.md +++ b/tracing_duper/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Bump `duper` to 0.6.0. + ## 0.1.0 (2025-12-07) Initial release. diff --git a/tracing_duper/Cargo.toml b/tracing_duper/Cargo.toml index 2879c55..72e5d7f 100644 --- a/tracing_duper/Cargo.toml +++ b/tracing_duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tracing_duper" description = "A tracing subscriber for the Duper format." -version = "0.1.0" +version = "0.1.1" edition = "2024" rust-version = "1.88" license = "MIT" @@ -18,7 +18,7 @@ chrono = ["dep:chrono"] [dependencies] chrono = { version = "0.4", optional = true } -duper = { version = "0.5", path = "../duper" } +duper = { version = "0.6", path = "../duper" } tracing-core = "0.1" tracing-subscriber = { version = "0.3", default-features = false, features = [ "std", diff --git a/tracing_duper/src/lib.rs b/tracing_duper/src/lib.rs index a3061cd..21326d1 100644 --- a/tracing_duper/src/lib.rs +++ b/tracing_duper/src/lib.rs @@ -66,7 +66,7 @@ use std::{ #[cfg(feature = "chrono")] use chrono::{Local, Utc}; -use duper::{DuperIdentifier, DuperKey, DuperObject, DuperValue, Serializer}; +use duper::{DuperFloat, DuperIdentifier, DuperKey, DuperObject, DuperValue, Serializer}; use tracing_core::{Event, Subscriber, field}; use tracing_subscriber::{Layer, field::VisitOutput, registry::LookupSpan}; @@ -661,13 +661,15 @@ impl tracing_core::field::Visit for DuperVisitor<'_> { if self.values.contains_key(&key) { return; } - self.values.insert( - key, - DuperValue::Float { - identifier: None, - inner: value, - }, - ); + if let Ok(value) = DuperFloat::try_new(value) { + self.values.insert( + key, + DuperValue::Float { + identifier: None, + inner: value, + }, + ); + } } fn record_i64(&mut self, field: &tracing_core::Field, value: i64) {