From 964721239e04a06f0850542055514d1312ddff12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 17 Apr 2025 12:32:25 +0200 Subject: [PATCH 1/5] ser: make tracing Serializer pub --- serde-reflection/src/lib.rs | 1 + serde-reflection/src/ser.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/serde-reflection/src/lib.rs b/serde-reflection/src/lib.rs index 4de1c3332..7953e725c 100644 --- a/serde-reflection/src/lib.rs +++ b/serde-reflection/src/lib.rs @@ -365,3 +365,4 @@ pub use error::{Error, Result}; pub use format::{ContainerFormat, Format, FormatHolder, Named, Variable, VariantFormat}; pub use trace::{Registry, Samples, Tracer, TracerConfig}; pub use value::Value; +pub use ser::Serializer; diff --git a/serde-reflection/src/ser.rs b/serde-reflection/src/ser.rs index f221878dc..a7b1c387b 100644 --- a/serde-reflection/src/ser.rs +++ b/serde-reflection/src/ser.rs @@ -12,13 +12,13 @@ use serde::{ser, Serialize}; /// Serialize a single value. /// The lifetime 'a is set by the serialization call site and the `&'a mut` /// references used to return tracing results and serialization samples. -pub(crate) struct Serializer<'a> { +pub struct Serializer<'a> { tracer: &'a mut Tracer, samples: &'a mut Samples, } impl<'a> Serializer<'a> { - pub(crate) fn new(tracer: &'a mut Tracer, samples: &'a mut Samples) -> Self { + pub fn new(tracer: &'a mut Tracer, samples: &'a mut Samples) -> Self { Self { tracer, samples } } } From 07c00032f87c75c273707800ba54e3e876de2fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 17 Apr 2025 16:29:22 +0200 Subject: [PATCH 2/5] de: make tracing Deserializer pub --- serde-reflection/src/de.rs | 4 ++-- serde-reflection/src/lib.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/serde-reflection/src/de.rs b/serde-reflection/src/de.rs index c08baa634..f0e21db9e 100644 --- a/serde-reflection/src/de.rs +++ b/serde-reflection/src/de.rs @@ -20,14 +20,14 @@ use std::collections::btree_map::{BTreeMap, Entry}; /// `&'a mut` references used to return tracing results. /// * The lifetime 'de is fixed and the `&'de` reference meant to let us /// borrow values from previous serialization runs. -pub(crate) struct Deserializer<'de, 'a> { +pub struct Deserializer<'de, 'a> { tracer: &'a mut Tracer, samples: &'de Samples, format: &'a mut Format, } impl<'de, 'a> Deserializer<'de, 'a> { - pub(crate) fn new( + pub fn new( tracer: &'a mut Tracer, samples: &'de Samples, format: &'a mut Format, diff --git a/serde-reflection/src/lib.rs b/serde-reflection/src/lib.rs index 7953e725c..4ab8f2a1f 100644 --- a/serde-reflection/src/lib.rs +++ b/serde-reflection/src/lib.rs @@ -366,3 +366,4 @@ pub use format::{ContainerFormat, Format, FormatHolder, Named, Variable, Variant pub use trace::{Registry, Samples, Tracer, TracerConfig}; pub use value::Value; pub use ser::Serializer; +pub use de::Deserializer; From c9007dd986a35cfa086f335f759a30875dddf9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 17 Apr 2025 16:29:44 +0200 Subject: [PATCH 3/5] tracer: make Tracer.incomplete_enums pub [wip] --- serde-reflection/src/trace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde-reflection/src/trace.rs b/serde-reflection/src/trace.rs index b3fcf3b46..a1efdc9a3 100644 --- a/serde-reflection/src/trace.rs +++ b/serde-reflection/src/trace.rs @@ -30,14 +30,14 @@ pub struct Tracer { /// Enums that have detected to be yet incomplete (i.e. missing variants) /// while tracing deserialization. - pub(crate) incomplete_enums: BTreeMap, + pub incomplete_enums: BTreeMap, /// Discriminant associated with each variant of each enum. pub(crate) discriminants: BTreeMap<(TypeId, VariantId<'static>), Discriminant>, } #[derive(Copy, Clone, Debug)] -pub(crate) enum EnumProgress { +pub enum EnumProgress { /// There are variant names that have not yet been traced. NamedVariantsRemaining, /// There are variant numbers that have not yet been traced. From 9d81ac04f7f7bde86b7b88184fa5eb51b708b5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 18 Apr 2025 16:45:03 +0200 Subject: [PATCH 4/5] fmt --- serde-reflection/src/de.rs | 6 +----- serde-reflection/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/serde-reflection/src/de.rs b/serde-reflection/src/de.rs index f0e21db9e..28937a716 100644 --- a/serde-reflection/src/de.rs +++ b/serde-reflection/src/de.rs @@ -27,11 +27,7 @@ pub struct Deserializer<'de, 'a> { } impl<'de, 'a> Deserializer<'de, 'a> { - pub fn new( - tracer: &'a mut Tracer, - samples: &'de Samples, - format: &'a mut Format, - ) -> Self { + pub fn new(tracer: &'a mut Tracer, samples: &'de Samples, format: &'a mut Format) -> Self { Deserializer { tracer, samples, diff --git a/serde-reflection/src/lib.rs b/serde-reflection/src/lib.rs index 4ab8f2a1f..4d48774c5 100644 --- a/serde-reflection/src/lib.rs +++ b/serde-reflection/src/lib.rs @@ -361,9 +361,9 @@ mod ser; mod trace; mod value; +pub use de::Deserializer; pub use error::{Error, Result}; pub use format::{ContainerFormat, Format, FormatHolder, Named, Variable, VariantFormat}; +pub use ser::Serializer; pub use trace::{Registry, Samples, Tracer, TracerConfig}; pub use value::Value; -pub use ser::Serializer; -pub use de::Deserializer; From 2910c9c3422ee7b7fe2bffe7e1f929778b217607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 5 May 2025 12:27:07 +0200 Subject: [PATCH 5/5] trace: expose pend_enum() Introduces a third EnumProgress variant to mark enums as pending re-analysis. This now keeps them marked as incomplete but lets the Deserialize make progress. It upholds the promise the the registry can only be complete if incomplete_enums is empty. * add docs to Serializer::new, Deserializer::new * expose EnumProgress * make incomplete_enums only pub(crate) again --- serde-reflection/src/de.rs | 9 ++++++--- serde-reflection/src/lib.rs | 2 +- serde-reflection/src/ser.rs | 1 + serde-reflection/src/trace.rs | 37 ++++++++++++++++++++++++++++++----- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/serde-reflection/src/de.rs b/serde-reflection/src/de.rs index 28937a716..d7707f4b8 100644 --- a/serde-reflection/src/de.rs +++ b/serde-reflection/src/de.rs @@ -27,6 +27,7 @@ pub struct Deserializer<'de, 'a> { } impl<'de, 'a> Deserializer<'de, 'a> { + /// Create a new Deserializer pub fn new(tracer: &'a mut Tracer, samples: &'de Samples, format: &'a mut Format) -> Self { Deserializer { tracer, @@ -418,9 +419,11 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { _ => unreachable!(), }; - // If the enum is already marked as incomplete, visit the first index, hoping - // to avoid recursion. - if self.tracer.incomplete_enums.contains_key(enum_name) { + // If the enum is already marked as incomplete and not pending, visit the first index, + // hoping to avoid recursion. + if let Some(EnumProgress::IndexedVariantsRemaining | EnumProgress::NamedVariantsRemaining) = + self.tracer.incomplete_enums.get(enum_name) + { return visitor.visit_enum(EnumDeserializer::new( self.tracer, self.samples, diff --git a/serde-reflection/src/lib.rs b/serde-reflection/src/lib.rs index 4d48774c5..580d3847f 100644 --- a/serde-reflection/src/lib.rs +++ b/serde-reflection/src/lib.rs @@ -365,5 +365,5 @@ pub use de::Deserializer; pub use error::{Error, Result}; pub use format::{ContainerFormat, Format, FormatHolder, Named, Variable, VariantFormat}; pub use ser::Serializer; -pub use trace::{Registry, Samples, Tracer, TracerConfig}; +pub use trace::{EnumProgress, Registry, Samples, Tracer, TracerConfig}; pub use value::Value; diff --git a/serde-reflection/src/ser.rs b/serde-reflection/src/ser.rs index a7b1c387b..7b15e28d4 100644 --- a/serde-reflection/src/ser.rs +++ b/serde-reflection/src/ser.rs @@ -18,6 +18,7 @@ pub struct Serializer<'a> { } impl<'a> Serializer<'a> { + /// Create a new Serializer pub fn new(tracer: &'a mut Tracer, samples: &'a mut Samples) -> Self { Self { tracer, samples } } diff --git a/serde-reflection/src/trace.rs b/serde-reflection/src/trace.rs index a1efdc9a3..480c278b3 100644 --- a/serde-reflection/src/trace.rs +++ b/serde-reflection/src/trace.rs @@ -30,18 +30,21 @@ pub struct Tracer { /// Enums that have detected to be yet incomplete (i.e. missing variants) /// while tracing deserialization. - pub incomplete_enums: BTreeMap, + pub(crate) incomplete_enums: BTreeMap, /// Discriminant associated with each variant of each enum. pub(crate) discriminants: BTreeMap<(TypeId, VariantId<'static>), Discriminant>, } +/// Type of untraced enum variants #[derive(Copy, Clone, Debug)] pub enum EnumProgress { /// There are variant names that have not yet been traced. NamedVariantsRemaining, /// There are variant numbers that have not yet been traced. IndexedVariantsRemaining, + /// Tracing of further variants is pending. + Pending, } #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)] @@ -243,6 +246,24 @@ impl Tracer { Ok((format, value)) } + /// Enable tracing of further variants of a incomplete enum. + /// + /// Marks an enum name as pending in the map of incomplete enums + /// and returns which type of variant tracing still needs to be performed. + /// + /// Call this in order to (simultaneously): + /// + /// * determine whether all variants of an enum have been traced, + /// * determine which type of variant tracing ([`EnumProgress`]) still needs to be + /// performed, and + /// * allow `Deserializer`/`trace_type_once` to make progress on a top level enum by + /// enabling tracing the next variant. + pub fn pend_enum(&mut self, name: &str) -> Option { + self.incomplete_enums + .get_mut(name) + .map(|p| std::mem::replace(p, EnumProgress::Pending)) + } + /// Same as `trace_type_once` but if `T` is an enum, we repeat the process /// until all variants of `T` are covered. /// We accumulate and return all the sampled values at the end. @@ -255,9 +276,12 @@ impl Tracer { let (format, value) = self.trace_type_once::(samples)?; values.push(value); if let Format::TypeName(name) = &format { - if let Some(&progress) = self.incomplete_enums.get(name) { + if let Some(progress) = self.pend_enum(name) { + debug_assert!( + !matches!(progress, EnumProgress::Pending), + "failed to make progress tracing enum {name}" + ); // Restart the analysis to find more variants of T. - self.incomplete_enums.remove(name); if let EnumProgress::NamedVariantsRemaining = progress { values.pop().unwrap(); } @@ -294,9 +318,12 @@ impl Tracer { let (format, value) = self.trace_type_once_with_seed(samples, seed.clone())?; values.push(value); if let Format::TypeName(name) = &format { - if let Some(&progress) = self.incomplete_enums.get(name) { + if let Some(progress) = self.pend_enum(name) { + debug_assert!( + !matches!(progress, EnumProgress::Pending), + "failed to make progress tracing enum {name}" + ); // Restart the analysis to find more variants of T. - self.incomplete_enums.remove(name); if let EnumProgress::NamedVariantsRemaining = progress { values.pop().unwrap(); }