Skip to content

Commit

Permalink
Add config option to not cut tracing at recursion points
Browse files Browse the repository at this point in the history
  • Loading branch information
juntyr committed Jun 6, 2024
1 parent d84ec9f commit 21a61ab
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 4 deletions.
14 changes: 10 additions & 4 deletions serde-reflection/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down
60 changes: 60 additions & 0 deletions serde-reflection/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>,
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 {
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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<u8>);

/// 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 {
Expand Down Expand Up @@ -245,6 +285,26 @@ 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<T>)>
where
T: Deserialize<'de>,
{
let mut values = Vec::new();
loop {
let (format, value) = self.trace_type_once::<T>(samples)?;
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.
Expand Down

0 comments on commit 21a61ab

Please sign in to comment.