serde_reflection/
trace.rs

1// Copyright (c) Facebook, Inc. and its affiliates
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{
5    de::Deserializer,
6    error::{Error, Result},
7    format::*,
8    ser::Serializer,
9    value::Value,
10};
11use once_cell::sync::Lazy;
12use serde::{de::DeserializeSeed, Deserialize, Serialize};
13use std::collections::{BTreeMap, BTreeSet};
14
15/// A map of container formats.
16pub type Registry = BTreeMap<String, ContainerFormat>;
17
18/// Structure to drive the tracing of Serde serialization and deserialization.
19/// This typically aims at computing a `Registry`.
20#[derive(Debug)]
21pub struct Tracer {
22    /// Hold configuration options.
23    pub(crate) config: TracerConfig,
24
25    /// Formats of the named containers discovered so far, while tracing
26    /// serialization and/or deserialization.
27    pub(crate) registry: Registry,
28
29    /// Enums that have detected to be yet incomplete (i.e. missing variants)
30    /// while tracing deserialization.
31    pub(crate) incomplete_enums: BTreeSet<String>,
32}
33
34/// User inputs, aka "samples", recorded during serialization.
35/// This will help passing user-defined checks during deserialization.
36#[derive(Debug, Default)]
37pub struct Samples {
38    pub(crate) values: BTreeMap<&'static str, Value>,
39}
40
41impl Samples {
42    /// Create a new structure to hold value samples.
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Obtain a (serialized) sample.
48    pub fn value(&self, name: &'static str) -> Option<&Value> {
49        self.values.get(name)
50    }
51}
52
53/// Configuration object to create a tracer.
54#[derive(Debug)]
55pub struct TracerConfig {
56    pub(crate) is_human_readable: bool,
57    pub(crate) record_samples_for_newtype_structs: bool,
58    pub(crate) record_samples_for_tuple_structs: bool,
59    pub(crate) record_samples_for_structs: bool,
60}
61
62impl Default for TracerConfig {
63    /// Create a new structure to hold value samples.
64    fn default() -> Self {
65        Self {
66            is_human_readable: false,
67            record_samples_for_newtype_structs: true,
68            record_samples_for_tuple_structs: false,
69            record_samples_for_structs: false,
70        }
71    }
72}
73
74impl TracerConfig {
75    /// Whether to trace the human readable encoding of (de)serialization.
76    #[allow(clippy::wrong_self_convention)]
77    pub fn is_human_readable(mut self, value: bool) -> Self {
78        self.is_human_readable = value;
79        self
80    }
81
82    /// Record samples of newtype structs during serialization and inject them during deserialization.
83    pub fn record_samples_for_newtype_structs(mut self, value: bool) -> Self {
84        self.record_samples_for_newtype_structs = value;
85        self
86    }
87
88    /// Record samples of tuple structs during serialization and inject them during deserialization.
89    pub fn record_samples_for_tuple_structs(mut self, value: bool) -> Self {
90        self.record_samples_for_tuple_structs = value;
91        self
92    }
93
94    /// Record samples of (regular) structs during serialization and inject them during deserialization.
95    pub fn record_samples_for_structs(mut self, value: bool) -> Self {
96        self.record_samples_for_structs = value;
97        self
98    }
99}
100
101impl Tracer {
102    /// Start tracing deserialization.
103    pub fn new(config: TracerConfig) -> Self {
104        Self {
105            config,
106            registry: BTreeMap::new(),
107            incomplete_enums: BTreeSet::new(),
108        }
109    }
110
111    /// Trace the serialization of a particular value.
112    /// * Nested containers will be added to the tracing registry, indexed by
113    /// their (non-qualified) name.
114    /// * Sampled Rust values will be inserted into `samples` to benefit future calls
115    /// to the `trace_type_*` methods.
116    pub fn trace_value<T>(&mut self, samples: &mut Samples, value: &T) -> Result<(Format, Value)>
117    where
118        T: ?Sized + Serialize,
119    {
120        let serializer = Serializer::new(self, samples);
121        let (mut format, sample) = value.serialize(serializer)?;
122        format.reduce();
123        Ok((format, sample))
124    }
125
126    /// Trace a single deserialization of a particular type.
127    /// * Nested containers will be added to the tracing registry, indexed by
128    /// their (non-qualified) name.
129    /// * As a byproduct of deserialization, we also return a value of type `T`.
130    /// * Tracing deserialization of a type may fail if this type or some dependencies
131    /// have implemented a custom deserializer that validates data. The solution is
132    /// to make sure that `samples` holds enough sampled Rust values to cover all the
133    /// custom types.
134    pub fn trace_type_once<'de, T>(&mut self, samples: &'de Samples) -> Result<(Format, T)>
135    where
136        T: Deserialize<'de>,
137    {
138        let mut format = Format::unknown();
139        let deserializer = Deserializer::new(self, samples, &mut format);
140        let value = T::deserialize(deserializer)?;
141        format.reduce();
142        Ok((format, value))
143    }
144
145    /// Same as `trace_type_once` for seeded deserialization.
146    pub fn trace_type_once_with_seed<'de, S>(
147        &mut self,
148        samples: &'de Samples,
149        seed: S,
150    ) -> Result<(Format, S::Value)>
151    where
152        S: DeserializeSeed<'de>,
153    {
154        let mut format = Format::unknown();
155        let deserializer = Deserializer::new(self, samples, &mut format);
156        let value = seed.deserialize(deserializer)?;
157        format.reduce();
158        Ok((format, value))
159    }
160
161    /// Same as `trace_type_once` but if `T` is an enum, we repeat the process
162    /// until all variants of `T` are covered.
163    /// We accumulate and return all the sampled values at the end.
164    pub fn trace_type<'de, T>(&mut self, samples: &'de Samples) -> Result<(Format, Vec<T>)>
165    where
166        T: Deserialize<'de>,
167    {
168        let mut values = Vec::new();
169        loop {
170            let (format, value) = self.trace_type_once::<T>(samples)?;
171            values.push(value);
172            if let Format::TypeName(name) = &format {
173                if self.incomplete_enums.contains(name) {
174                    // Restart the analysis to find more variants of T.
175                    self.incomplete_enums.remove(name);
176                    continue;
177                }
178            }
179            return Ok((format, values));
180        }
181    }
182
183    /// Trace a type `T` that is simple enough that no samples of values are needed.
184    /// * If `T` is an enum, the tracing iterates until all variants of `T` are covered.
185    /// * Accumulate and return all the sampled values at the end.
186    /// This is merely a shortcut for `self.trace_type` with a fixed empty set of samples.
187    pub fn trace_simple_type<'de, T>(&mut self) -> Result<(Format, Vec<T>)>
188    where
189        T: Deserialize<'de>,
190    {
191        static SAMPLES: Lazy<Samples> = Lazy::new(Samples::new);
192        self.trace_type(&SAMPLES)
193    }
194
195    /// Same as `trace_type` for seeded deserialization.
196    pub fn trace_type_with_seed<'de, S>(
197        &mut self,
198        samples: &'de Samples,
199        seed: S,
200    ) -> Result<(Format, Vec<S::Value>)>
201    where
202        S: DeserializeSeed<'de> + Clone,
203    {
204        let mut values = Vec::new();
205        loop {
206            let (format, value) = self.trace_type_once_with_seed(samples, seed.clone())?;
207            values.push(value);
208            if let Format::TypeName(name) = &format {
209                if self.incomplete_enums.contains(name) {
210                    // Restart the analysis to find more variants of T.
211                    self.incomplete_enums.remove(name);
212                    continue;
213                }
214            }
215            return Ok((format, values));
216        }
217    }
218
219    /// Finish tracing and recover a map of normalized formats.
220    /// Returns an error if we detect incompletely traced types.
221    /// This may happen in a few of cases:
222    /// * We traced serialization of user-provided values but we are still missing the content
223    ///   of an option type, the content of a sequence type, the key or the value of a dictionary type.
224    /// * We traced deserialization of an enum type but we detect that some enum variants are still missing.
225    pub fn registry(self) -> Result<Registry> {
226        let mut registry = self.registry;
227        for (name, format) in registry.iter_mut() {
228            format
229                .normalize()
230                .map_err(|_| Error::UnknownFormatInContainer(name.clone()))?;
231        }
232        if self.incomplete_enums.is_empty() {
233            Ok(registry)
234        } else {
235            Err(Error::MissingVariants(
236                self.incomplete_enums.into_iter().collect(),
237            ))
238        }
239    }
240
241    /// Same as registry but always return a value, even if we detected issues.
242    /// This should only be use for debugging.
243    pub fn registry_unchecked(self) -> Registry {
244        let mut registry = self.registry;
245        for format in registry.values_mut() {
246            format.normalize().unwrap_or(());
247        }
248        registry
249    }
250
251    pub(crate) fn record_container(
252        &mut self,
253        samples: &mut Samples,
254        name: &'static str,
255        format: ContainerFormat,
256        value: Value,
257        record_value: bool,
258    ) -> Result<(Format, Value)> {
259        self.registry.entry(name.to_string()).unify(format)?;
260        if record_value {
261            samples.values.insert(name, value.clone());
262        }
263        Ok((Format::TypeName(name.into()), value))
264    }
265
266    pub(crate) fn record_variant(
267        &mut self,
268        samples: &mut Samples,
269        name: &'static str,
270        variant_index: u32,
271        variant_name: &'static str,
272        variant: VariantFormat,
273        variant_value: Value,
274    ) -> Result<(Format, Value)> {
275        let mut variants = BTreeMap::new();
276        variants.insert(
277            variant_index,
278            Named {
279                name: variant_name.into(),
280                value: variant,
281            },
282        );
283        let format = ContainerFormat::Enum(variants);
284        let value = Value::Variant(variant_index, Box::new(variant_value));
285        self.record_container(samples, name, format, value, false)
286    }
287
288    pub(crate) fn get_sample<'de, 'a>(
289        &'a self,
290        samples: &'de Samples,
291        name: &'static str,
292    ) -> Option<(&'a ContainerFormat, &'de Value)> {
293        match samples.value(name) {
294            Some(value) => {
295                let format = self
296                    .registry
297                    .get(name)
298                    .expect("recorded containers should have a format already");
299                Some((format, value))
300            }
301            None => None,
302        }
303    }
304}