From af50546a115df39d62e2dd3990836d843a7d74a4 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 07:38:06 +0100 Subject: [PATCH 1/9] add Cargo dependencies --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index addb91b..d92429c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,7 @@ num-traits = "0.2.14" bit-set = "0.5.0" gcollections = "1.5.0" trilean = "1.1.0" +serde = "1.0.219" + +[dev-dependencies] +serde_test = "1.0.177" From 98535cffbe494853757b8b649d9229db5f786eb2 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 07:38:11 +0100 Subject: [PATCH 2/9] implement basic serialize/deserialize for interval --- src/libinterval/interval.rs | 109 +++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/src/libinterval/interval.rs b/src/libinterval/interval.rs index e07ba3a..d87fcc5 100644 --- a/src/libinterval/interval.rs +++ b/src/libinterval/interval.rs @@ -37,11 +37,15 @@ use crate::ops::*; use gcollections::ops::*; use gcollections::*; +use serde::de::{self, SeqAccess, Visitor}; +use serde::ser::SerializeTuple; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use trilean::SKleene; use num_traits::{Num, Zero}; use std::cmp::{max, min}; -use std::fmt::{Display, Error, Formatter}; +use std::fmt::{self, Display, Error, Formatter}; +use std::marker::PhantomData; use std::ops::{Add, Mul, Sub}; /// Closed interval (endpoints included). @@ -51,6 +55,65 @@ pub struct Interval { ub: Bound, } +impl Serialize for Interval +where + Bound: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tuple = serializer.serialize_tuple(2)?; + tuple.serialize_element(&self.lb)?; + tuple.serialize_element(&self.ub)?; + tuple.end() + } +} + +impl<'de, Bound> Deserialize<'de> for Interval +where + Bound: Width + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct IntervalVisitor { + marker: PhantomData Bound>, + } + impl IntervalVisitor { + fn new() -> Self { + IntervalVisitor { + marker: PhantomData, + } + } + } + impl<'de, Bound> Visitor<'de> for IntervalVisitor + where + Bound: Width + Deserialize<'de>, + { + type Value = Interval; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("tuple of two integers") + } + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let lower = seq + .next_element::()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let upper = seq + .next_element::()? + .ok_or_else(|| de::Error::invalid_length(1, &self))?; + Ok(Interval::new(lower, upper)) + } + } + deserializer.deserialize_tuple(2, IntervalVisitor::::new()) + } +} + impl IntervalKind for Interval {} impl Collection for Interval { @@ -1371,6 +1434,8 @@ where #[allow(non_upper_case_globals)] #[cfg(test)] mod tests { + use serde_test::{assert_tokens, Token}; + use super::*; const empty: Interval = Interval { lb: 1, ub: 0 }; @@ -2334,4 +2399,46 @@ mod tests { ); tester.test_all(); } + + #[test] + fn test_ser_de_interval() { + let interval = Interval::new(10, 20); + assert_tokens( + &interval, + &[ + Token::Tuple { len: 2 }, + Token::I32(10), + Token::I32(20), + Token::TupleEnd, + ], + ); + } + + #[test] + fn test_ser_de_interval_u8() { + let interval = Interval::::new(10, 20); + assert_tokens( + &interval, + &[ + Token::Tuple { len: 2 }, + Token::U8(10), + Token::U8(20), + Token::TupleEnd, + ], + ); + } + + #[test] + fn test_ser_de_interval_i64() { + let interval = Interval::::whole(); + assert_tokens( + &interval, + &[ + Token::Tuple { len: 2 }, + Token::I64(::min_value()), + Token::I64(::max_value()), + Token::TupleEnd, + ], + ); + } } From 607a1cb75dc3b0353b35b3fc9dcedacb9bd4c121 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 08:03:30 +0100 Subject: [PATCH 3/9] test mixed types deserialization and error messages --- src/libinterval/interval.rs | 55 +++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/libinterval/interval.rs b/src/libinterval/interval.rs index d87fcc5..d8f10eb 100644 --- a/src/libinterval/interval.rs +++ b/src/libinterval/interval.rs @@ -107,10 +107,17 @@ where let upper = seq .next_element::()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; + let mut extra_elements = 0; + while seq.next_element::()?.is_some() { + extra_elements += 1; + } + if extra_elements > 0 { + return Err(de::Error::invalid_length(2 + extra_elements, &self)); + } Ok(Interval::new(lower, upper)) } } - deserializer.deserialize_tuple(2, IntervalVisitor::::new()) + deserializer.deserialize_any(IntervalVisitor::::new()) } } @@ -1434,7 +1441,7 @@ where #[allow(non_upper_case_globals)] #[cfg(test)] mod tests { - use serde_test::{assert_tokens, Token}; + use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; use super::*; @@ -2414,6 +2421,50 @@ mod tests { ); } + #[test] + fn test_de_interval_mixed_types() { + let interval = Interval::new(-5, 15); + assert_de_tokens::>( + &interval, + &[ + Token::Tuple { len: 2 }, + Token::I32(-5), + Token::I64(15), + Token::TupleEnd, + ], + ); + } + + #[test] + fn test_de_interval_extra_token() { + assert_de_tokens_error::>( + &[ + Token::Tuple { len: 3 }, + Token::I32(10), + Token::I32(20), + Token::I32(30), + Token::TupleEnd, + ], + "invalid length 3, expected tuple of two integers", + ); + } + + #[test] + fn test_de_interval_extra_tokens() { + assert_de_tokens_error::>( + &[ + Token::Tuple { len: 5 }, + Token::I32(10), + Token::I32(20), + Token::I32(30), + Token::I32(40), + Token::I32(50), + Token::TupleEnd, + ], + "invalid length 5, expected tuple of two integers", + ); + } + #[test] fn test_ser_de_interval_u8() { let interval = Interval::::new(10, 20); From 0ad02557b7b4be010eaec19c092a353c9334dc4e Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 10:18:01 +0100 Subject: [PATCH 4/9] implement serialize/deserialize for singleton interval --- src/libinterval/interval.rs | 107 +++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 7 deletions(-) diff --git a/src/libinterval/interval.rs b/src/libinterval/interval.rs index d8f10eb..1fb05a3 100644 --- a/src/libinterval/interval.rs +++ b/src/libinterval/interval.rs @@ -44,6 +44,7 @@ use trilean::SKleene; use num_traits::{Num, Zero}; use std::cmp::{max, min}; +use std::convert::TryFrom; use std::fmt::{self, Display, Error, Formatter}; use std::marker::PhantomData; use std::ops::{Add, Mul, Sub}; @@ -57,22 +58,28 @@ pub struct Interval { impl Serialize for Interval where - Bound: Serialize, + Bound: Serialize + Width + Num, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - let mut tuple = serializer.serialize_tuple(2)?; - tuple.serialize_element(&self.lb)?; - tuple.serialize_element(&self.ub)?; - tuple.end() + if self.is_singleton() { + self.lb.serialize(serializer) + } else { + let mut tuple = serializer.serialize_tuple(2)?; + tuple.serialize_element(&self.lb)?; + tuple.serialize_element(&self.ub)?; + tuple.end() + } } } impl<'de, Bound> Deserialize<'de> for Interval where - Bound: Width + Deserialize<'de>, + Bound: Width + Deserialize<'de> + TryFrom + TryFrom, + >::Error: std::fmt::Display, + >::Error: std::fmt::Display, { fn deserialize(deserializer: D) -> Result where @@ -90,7 +97,9 @@ where } impl<'de, Bound> Visitor<'de> for IntervalVisitor where - Bound: Width + Deserialize<'de>, + Bound: Width + Deserialize<'de> + TryFrom + TryFrom, + >::Error: std::fmt::Display, + >::Error: std::fmt::Display, { type Value = Interval; @@ -116,6 +125,52 @@ where } Ok(Interval::new(lower, upper)) } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + let v = Bound::try_from(v).map_err(|e| { + E::custom(format!( + "integer {} cannot be represented as target type: {}", + v, e + )) + })?; + if v < ::min_value() { + Err(de::Error::custom( + "Lower bound exceeds the minimum value of a bound.", + )) + } else if v > ::max_value() { + Err(de::Error::custom( + "Upper bound exceeds the maximum value of a bound.", + )) + } else { + Ok(Interval::singleton(Bound::from(v))) + } + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + let v = Bound::try_from(v).map_err(|e| { + E::custom(format!( + "integer {} cannot be represented as target type: {}", + v, e + )) + })?; + if v < ::min_value() { + Err(de::Error::custom( + "Lower bound exceeds the minimum value of a bound.", + )) + } else if v > ::max_value() { + Err(de::Error::custom( + "Upper bound exceeds the maximum value of a bound.", + )) + } else { + Ok(Interval::singleton(Bound::from(v))) + } + } } deserializer.deserialize_any(IntervalVisitor::::new()) } @@ -2492,4 +2547,42 @@ mod tests { ], ); } + + #[test] + fn test_ser_de_interval_singleton() { + let interval = Interval::singleton(-12); + assert_tokens(&interval, &[Token::I32(-12)]); + } + + #[test] + fn test_ser_de_interval_singleton_type_change() { + let interval = Interval::singleton(-12); + assert_de_tokens(&interval, &[Token::I16(-12)]); + } + + #[test] + fn test_ser_de_interval_singleton_type_change_overflow_signed() { + assert_de_tokens_error::>( &[Token::I16(-129)], "integer -129 cannot be represented as target type: out of range integral type conversion attempted"); + } + + #[test] + fn test_ser_de_interval_singleton_type_change_overflow_unsigned() { + assert_de_tokens_error::>( &[Token::U16(256)], "integer 256 cannot be represented as target type: out of range integral type conversion attempted"); + } + + #[test] + fn test_ser_de_interval_singleton_type_change_exceeds_lower() { + assert_de_tokens_error::>( + &[Token::I64(i32::MIN as i64)], + "Lower bound exceeds the minimum value of a bound.", + ); + } + + #[test] + fn test_ser_de_interval_singleton_type_change_exceeds_upper() { + assert_de_tokens_error::>( + &[Token::U64(u32::MAX as u64)], + "Upper bound exceeds the maximum value of a bound.", + ); + } } From 33c23230e8a3cdb2e9224c9317306f4219ead0ad Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 10:26:32 +0100 Subject: [PATCH 5/9] implement serialize/deserialize for empty interval --- src/libinterval/interval.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/libinterval/interval.rs b/src/libinterval/interval.rs index 1fb05a3..e5700d0 100644 --- a/src/libinterval/interval.rs +++ b/src/libinterval/interval.rs @@ -64,7 +64,9 @@ where where S: Serializer, { - if self.is_singleton() { + if self.is_empty() { + serializer.serialize_none() + } else if self.is_singleton() { self.lb.serialize(serializer) } else { let mut tuple = serializer.serialize_tuple(2)?; @@ -77,7 +79,7 @@ where impl<'de, Bound> Deserialize<'de> for Interval where - Bound: Width + Deserialize<'de> + TryFrom + TryFrom, + Bound: Width + Num + Deserialize<'de> + TryFrom + TryFrom, >::Error: std::fmt::Display, >::Error: std::fmt::Display, { @@ -97,15 +99,16 @@ where } impl<'de, Bound> Visitor<'de> for IntervalVisitor where - Bound: Width + Deserialize<'de> + TryFrom + TryFrom, + Bound: Width + Deserialize<'de> + Num + TryFrom + TryFrom, >::Error: std::fmt::Display, >::Error: std::fmt::Display, { type Value = Interval; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("tuple of two integers") + formatter.write_str("tuple of two integers or single integer or none") } + fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, @@ -171,6 +174,13 @@ where Ok(Interval::singleton(Bound::from(v))) } } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(Interval::::empty()) + } } deserializer.deserialize_any(IntervalVisitor::::new()) } @@ -2500,7 +2510,7 @@ mod tests { Token::I32(30), Token::TupleEnd, ], - "invalid length 3, expected tuple of two integers", + "invalid length 3, expected tuple of two integers or single integer or none", ); } @@ -2516,7 +2526,7 @@ mod tests { Token::I32(50), Token::TupleEnd, ], - "invalid length 5, expected tuple of two integers", + "invalid length 5, expected tuple of two integers or single integer or none", ); } @@ -2585,4 +2595,10 @@ mod tests { "Upper bound exceeds the maximum value of a bound.", ); } + + #[test] + fn test_ser_de_empty_interval() { + let interval = Interval::::empty(); + assert_tokens(&interval, &[Token::None]); + } } From dfdc410b0f69e3be80a90f29d35d591cb528facf Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 10:34:51 +0100 Subject: [PATCH 6/9] reduce complexity by removing singleton specialization --- src/libinterval/interval.rs | 103 +++--------------------------------- 1 file changed, 6 insertions(+), 97 deletions(-) diff --git a/src/libinterval/interval.rs b/src/libinterval/interval.rs index e5700d0..a739a11 100644 --- a/src/libinterval/interval.rs +++ b/src/libinterval/interval.rs @@ -44,7 +44,6 @@ use trilean::SKleene; use num_traits::{Num, Zero}; use std::cmp::{max, min}; -use std::convert::TryFrom; use std::fmt::{self, Display, Error, Formatter}; use std::marker::PhantomData; use std::ops::{Add, Mul, Sub}; @@ -66,8 +65,6 @@ where { if self.is_empty() { serializer.serialize_none() - } else if self.is_singleton() { - self.lb.serialize(serializer) } else { let mut tuple = serializer.serialize_tuple(2)?; tuple.serialize_element(&self.lb)?; @@ -79,9 +76,7 @@ where impl<'de, Bound> Deserialize<'de> for Interval where - Bound: Width + Num + Deserialize<'de> + TryFrom + TryFrom, - >::Error: std::fmt::Display, - >::Error: std::fmt::Display, + Bound: Width + Num + Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where @@ -99,14 +94,12 @@ where } impl<'de, Bound> Visitor<'de> for IntervalVisitor where - Bound: Width + Deserialize<'de> + Num + TryFrom + TryFrom, - >::Error: std::fmt::Display, - >::Error: std::fmt::Display, + Bound: Width + Deserialize<'de> + Num, { type Value = Interval; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("tuple of two integers or single integer or none") + formatter.write_str("tuple of two numbers or none") } fn visit_seq(self, mut seq: A) -> Result @@ -129,52 +122,6 @@ where Ok(Interval::new(lower, upper)) } - fn visit_i64(self, v: i64) -> Result - where - E: de::Error, - { - let v = Bound::try_from(v).map_err(|e| { - E::custom(format!( - "integer {} cannot be represented as target type: {}", - v, e - )) - })?; - if v < ::min_value() { - Err(de::Error::custom( - "Lower bound exceeds the minimum value of a bound.", - )) - } else if v > ::max_value() { - Err(de::Error::custom( - "Upper bound exceeds the maximum value of a bound.", - )) - } else { - Ok(Interval::singleton(Bound::from(v))) - } - } - - fn visit_u64(self, v: u64) -> Result - where - E: de::Error, - { - let v = Bound::try_from(v).map_err(|e| { - E::custom(format!( - "integer {} cannot be represented as target type: {}", - v, e - )) - })?; - if v < ::min_value() { - Err(de::Error::custom( - "Lower bound exceeds the minimum value of a bound.", - )) - } else if v > ::max_value() { - Err(de::Error::custom( - "Upper bound exceeds the maximum value of a bound.", - )) - } else { - Ok(Interval::singleton(Bound::from(v))) - } - } - fn visit_none(self) -> Result where E: de::Error, @@ -182,7 +129,7 @@ where Ok(Interval::::empty()) } } - deserializer.deserialize_any(IntervalVisitor::::new()) + deserializer.deserialize_any(IntervalVisitor::new()) } } @@ -2510,7 +2457,7 @@ mod tests { Token::I32(30), Token::TupleEnd, ], - "invalid length 3, expected tuple of two integers or single integer or none", + "invalid length 3, expected tuple of two numbers or none", ); } @@ -2526,7 +2473,7 @@ mod tests { Token::I32(50), Token::TupleEnd, ], - "invalid length 5, expected tuple of two integers or single integer or none", + "invalid length 5, expected tuple of two numbers or none", ); } @@ -2558,44 +2505,6 @@ mod tests { ); } - #[test] - fn test_ser_de_interval_singleton() { - let interval = Interval::singleton(-12); - assert_tokens(&interval, &[Token::I32(-12)]); - } - - #[test] - fn test_ser_de_interval_singleton_type_change() { - let interval = Interval::singleton(-12); - assert_de_tokens(&interval, &[Token::I16(-12)]); - } - - #[test] - fn test_ser_de_interval_singleton_type_change_overflow_signed() { - assert_de_tokens_error::>( &[Token::I16(-129)], "integer -129 cannot be represented as target type: out of range integral type conversion attempted"); - } - - #[test] - fn test_ser_de_interval_singleton_type_change_overflow_unsigned() { - assert_de_tokens_error::>( &[Token::U16(256)], "integer 256 cannot be represented as target type: out of range integral type conversion attempted"); - } - - #[test] - fn test_ser_de_interval_singleton_type_change_exceeds_lower() { - assert_de_tokens_error::>( - &[Token::I64(i32::MIN as i64)], - "Lower bound exceeds the minimum value of a bound.", - ); - } - - #[test] - fn test_ser_de_interval_singleton_type_change_exceeds_upper() { - assert_de_tokens_error::>( - &[Token::U64(u32::MAX as u64)], - "Upper bound exceeds the maximum value of a bound.", - ); - } - #[test] fn test_ser_de_empty_interval() { let interval = Interval::::empty(); From dd1489cd51ffc8daa8af35e2bf0a03452635d129 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 11:20:15 +0100 Subject: [PATCH 7/9] implement basic serialize/deserialize for interval set --- src/libinterval/interval_set.rs | 107 ++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/libinterval/interval_set.rs b/src/libinterval/interval_set.rs index f382c2e..8972d7d 100644 --- a/src/libinterval/interval_set.rs +++ b/src/libinterval/interval_set.rs @@ -33,8 +33,14 @@ use crate::interval::ToInterval; use crate::ops::*; use gcollections::ops::*; use gcollections::*; +use serde::de::SeqAccess; +use serde::de::Visitor; +use serde::Deserialize; +use serde::Serialize; +use std::fmt; use std::fmt::{Display, Error, Formatter}; use std::iter::{IntoIterator, Peekable}; +use std::marker::PhantomData; use std::ops::{Add, Mul, Sub}; use trilean::SKleene; @@ -46,6 +52,67 @@ pub struct IntervalSet { size: Bound::Output, } +impl Serialize for IntervalSet +where + Bound: Width + Num + Serialize, + Interval: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq(&self.intervals) + } +} + +impl<'de, Bound> Deserialize<'de> for IntervalSet +where + Bound: Width + Num + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct IntervalSetVisitor { + marker: PhantomData Interval>, + } + impl IntervalSetVisitor { + fn new() -> Self { + IntervalSetVisitor { + marker: PhantomData, + } + } + } + impl<'de, Bound> Visitor<'de> for IntervalSetVisitor + where + Bound: Width + Deserialize<'de> + Num, + { + type Value = IntervalSet; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("sequence of intervals") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut intervals = Vec::new(); + if let Some(size) = seq.size_hint() { + intervals.reserve(size); + } + while let Some(interval) = seq.next_element::>()? { + intervals.push(interval); + } + let mut interval_set = IntervalSet::empty(); + interval_set.extend(intervals); + Ok(interval_set) + } + } + deserializer.deserialize_seq(IntervalSetVisitor::new()) + } +} + impl IntervalKind for IntervalSet {} impl Collection for IntervalSet { @@ -1357,6 +1424,8 @@ where #[allow(non_upper_case_globals)] #[cfg(test)] mod tests { + use serde_test::{assert_tokens, Token}; + use super::*; const extend_example: [(i32, i32); 2] = [(11, 33), (-55, -44)]; @@ -2749,4 +2818,42 @@ mod tests { ); } } + + #[test] + fn test_ser_de_single_interval_set() { + assert_tokens( + &IntervalSet::from_interval(Interval::new(8, 12)), + &[ + Token::Seq { len: Some(1) }, + Token::Tuple { len: 2 }, + Token::I32(8), + Token::I32(12), + Token::TupleEnd, + Token::SeqEnd, + ], + ); + } + + #[test] + fn test_ser_de_multiple_interval_set() { + assert_tokens( + &[(20, 21), (3, 5), (-10, -5)].to_interval_set(), + &[ + Token::Seq { len: Some(3) }, + Token::Tuple { len: 2 }, + Token::I32(-10), + Token::I32(-5), + Token::TupleEnd, + Token::Tuple { len: 2 }, + Token::I32(3), + Token::I32(5), + Token::TupleEnd, + Token::Tuple { len: 2 }, + Token::I32(20), + Token::I32(21), + Token::TupleEnd, + Token::SeqEnd, + ], + ); + } } From 0a3c27345318daef06a7bad4785c54af2804494f Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 11:26:16 +0100 Subject: [PATCH 8/9] implement serialize/deserialize for empty interval set --- src/libinterval/interval_set.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libinterval/interval_set.rs b/src/libinterval/interval_set.rs index 8972d7d..6a3016f 100644 --- a/src/libinterval/interval_set.rs +++ b/src/libinterval/interval_set.rs @@ -61,7 +61,11 @@ where where S: serde::Serializer, { - serializer.collect_seq(&self.intervals) + if self.is_empty() { + serializer.serialize_none() + } else { + serializer.collect_seq(&self.intervals) + } } } @@ -108,8 +112,15 @@ where interval_set.extend(intervals); Ok(interval_set) } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(IntervalSet::empty()) + } } - deserializer.deserialize_seq(IntervalSetVisitor::new()) + deserializer.deserialize_any(IntervalSetVisitor::new()) } } @@ -2856,4 +2867,9 @@ mod tests { ], ); } + + #[test] + fn test_ser_de_empty_interval_set() { + assert_tokens(&IntervalSet::::empty(), &[Token::None]); + } } From 146e5ac97ffd59c144bc1d1b499190ec659387a6 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sun, 4 May 2025 11:38:20 +0100 Subject: [PATCH 9/9] bump minor version number --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d92429c..a792590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "intervallum" -version = "1.4.2" +version = "1.4.3" authors = [ "Pierre Talbot " ] description = "Generic interval and interval set library." -documentation = "https://docs.rs/intervallum/1.4.2/interval/" +documentation = "https://docs.rs/intervallum/1.4.3/interval/" repository = "https://github.com/ptal/intervallum" readme = "README.md" keywords = ["interval", "math", "data-structure", "containers", "interval-set"]