diff --git a/serde-reflection/src/de.rs b/serde-reflection/src/de.rs index 9ee16213a..5b7b670e7 100644 --- a/serde-reflection/src/de.rs +++ b/serde-reflection/src/de.rs @@ -196,7 +196,7 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { let mut format = Format::unknown(); self.format .unify(Format::Option(Box::new(format.clone())))?; - if format.is_unknown() { + if format.is_unknown() || !self.tracer.config.cut_option_exploration { let inner = Deserializer::new(self.tracer, self.samples, &mut format); visitor.visit_some(inner) } else { @@ -262,7 +262,7 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { { let mut format = Format::unknown(); self.format.unify(Format::Seq(Box::new(format.clone())))?; - if format.is_unknown() { + if format.is_unknown() || !self.tracer.config.cut_seq_exploration { // Simulate vector of size 1. let inner = SeqDeserializer::new(self.tracer, self.samples, std::iter::once(&mut format)); @@ -329,7 +329,10 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { key: Box::new(key_format.clone()), value: Box::new(value_format.clone()), })?; - if key_format.is_unknown() || value_format.is_unknown() { + if key_format.is_unknown() + || value_format.is_unknown() + || !self.tracer.config.cut_map_exploration + { // Simulate a map with one entry. let inner = SeqDeserializer::new( self.tracer, @@ -413,7 +416,8 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { // If we have found all the variants OR if the enum is marked as // incomplete already, pick the first index. let index = if known_variants.len() == variants.len() - || self.tracer.incomplete_enums.contains(name) + || (self.tracer.incomplete_enums.contains(name) + && self.tracer.config.cut_enum_exploration) { 0 } else { @@ -436,6 +440,8 @@ impl<'de, 'a> de::Deserializer<'de> for Deserializer<'de, 'a> { // Mark the enum as incomplete if this was not the last variant to explore. if known_variants.len() != variants.len() { self.tracer.incomplete_enums.insert(name.into()); + } else { + self.tracer.incomplete_enums.remove(name); } // Compute the format for this variant. let inner = EnumDeserializer::new(self.tracer, self.samples, index, &mut value); diff --git a/serde-reflection/src/trace.rs b/serde-reflection/src/trace.rs index fe3347616..1dda883c3 100644 --- a/serde-reflection/src/trace.rs +++ b/serde-reflection/src/trace.rs @@ -75,6 +75,10 @@ pub struct TracerConfig { pub(crate) default_string_value: String, pub(crate) default_borrowed_bytes_value: &'static [u8], pub(crate) default_byte_buf_value: Vec, + pub(crate) cut_option_exploration: bool, + pub(crate) cut_seq_exploration: bool, + pub(crate) cut_map_exploration: bool, + pub(crate) cut_enum_exploration: bool, } impl Default for TracerConfig { @@ -103,6 +107,10 @@ impl Default for TracerConfig { default_string_value: String::new(), default_borrowed_bytes_value: b"", default_byte_buf_value: Vec::new(), + cut_option_exploration: true, + cut_seq_exploration: true, + cut_map_exploration: true, + cut_enum_exploration: true, } } } @@ -161,6 +169,38 @@ impl TracerConfig { define_default_value_setter!(default_string_value, String); define_default_value_setter!(default_borrowed_bytes_value, &'static [u8]); define_default_value_setter!(default_byte_buf_value, Vec); + + /// Whether an optional value is *not* explored again after encountering it once. + /// + /// Warning: Disabling this option may lead to the tracing not terminating. + pub fn cut_option_exploration(mut self, value: bool) -> Self { + self.cut_option_exploration = value; + self + } + + /// Whether a sequence is left empty after encountering it once. + /// + /// Warning: Disabling this option may lead to the tracing not terminating. + pub fn cut_seq_exploration(mut self, value: bool) -> Self { + self.cut_seq_exploration = value; + self + } + + /// Whether a map is left empty after encountering it once. + /// + /// Warning: Disabling this option may lead to the tracing not terminating. + pub fn cut_map_exploration(mut self, value: bool) -> Self { + self.cut_map_exploration = value; + self + } + + /// Whether an enum falls back to the first variant after encountering it once. + /// + /// Warning: Disabling this option may lead to the tracing not terminating. + pub fn cut_enum_exploration(mut self, value: bool) -> Self { + self.cut_enum_exploration = value; + self + } } impl Tracer { @@ -245,6 +285,47 @@ impl Tracer { } } + /// Same as `trace_type_once` but if any uncovered variants remain in the + /// recursive format, we repeat the process. + /// We accumulate and return all the sampled values at the end. + pub fn trace_type_all_variants<'de, T>( + &mut self, + samples: &'de Samples, + ) -> Result<(Format, Vec)> + where + T: Deserialize<'de>, + { + let mut values = Vec::new(); + loop { + let (format, value) = self.trace_type_once::(samples)?; + values.push(value); + if self.incomplete_enums.is_empty() { + return Ok((format, values)); + } + } + } + + /// Same as `trace_type_once` but if any uncovered variants remain in the + /// recursive format, we repeat the process. + /// We accumulate and return all the sampled values at the end. + pub fn trace_type_all_variants_with_seed<'de, S>( + &mut self, + samples: &'de Samples, + seed: S, + ) -> Result<(Format, Vec)> + where + S: DeserializeSeed<'de> + Clone, + { + let mut values = Vec::new(); + loop { + let (format, value) = self.trace_type_once_with_seed::(samples, seed.clone())?; + values.push(value); + if self.incomplete_enums.is_empty() { + return Ok((format, values)); + } + } + } + /// Trace a type `T` that is simple enough that no samples of values are needed. /// * If `T` is an enum, the tracing iterates until all variants of `T` are covered. /// * Accumulate and return all the sampled values at the end.