Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config option to not cut tracing at recursion points #41

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
81 changes: 81 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add "in case of recursive data-structures" ?

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,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<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() {
juntyr marked this conversation as resolved.
Show resolved Hide resolved
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<S::Value>)>
where
S: DeserializeSeed<'de> + Clone,
{
let mut values = Vec::new();
loop {
let (format, value) = self.trace_type_once_with_seed::<S>(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.
Expand Down
Loading