1use 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
15pub type Registry = BTreeMap<String, ContainerFormat>;
17
18#[derive(Debug)]
21pub struct Tracer {
22 pub(crate) config: TracerConfig,
24
25 pub(crate) registry: Registry,
28
29 pub(crate) incomplete_enums: BTreeSet<String>,
32}
33
34#[derive(Debug, Default)]
37pub struct Samples {
38 pub(crate) values: BTreeMap<&'static str, Value>,
39}
40
41impl Samples {
42 pub fn new() -> Self {
44 Self::default()
45 }
46
47 pub fn value(&self, name: &'static str) -> Option<&Value> {
49 self.values.get(name)
50 }
51}
52
53#[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 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 #[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 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 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 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 pub fn new(config: TracerConfig) -> Self {
104 Self {
105 config,
106 registry: BTreeMap::new(),
107 incomplete_enums: BTreeSet::new(),
108 }
109 }
110
111 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 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 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 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 self.incomplete_enums.remove(name);
176 continue;
177 }
178 }
179 return Ok((format, values));
180 }
181 }
182
183 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 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 self.incomplete_enums.remove(name);
212 continue;
213 }
214 }
215 return Ok((format, values));
216 }
217 }
218
219 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 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}