use crate::{
de::Deserializer,
error::{Error, Result},
format::*,
ser::Serializer,
value::Value,
};
use once_cell::sync::Lazy;
use serde::{de::DeserializeSeed, Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
pub type Registry = BTreeMap<String, ContainerFormat>;
#[derive(Debug)]
pub struct Tracer {
pub(crate) config: TracerConfig,
pub(crate) registry: Registry,
pub(crate) incomplete_enums: BTreeSet<String>,
}
#[derive(Debug, Default)]
pub struct Samples {
pub(crate) values: BTreeMap<&'static str, Value>,
}
impl Samples {
pub fn new() -> Self {
Self::default()
}
pub fn value(&self, name: &'static str) -> Option<&Value> {
self.values.get(name)
}
}
#[derive(Debug)]
pub struct TracerConfig {
pub(crate) is_human_readable: bool,
pub(crate) record_samples_for_newtype_structs: bool,
pub(crate) record_samples_for_tuple_structs: bool,
pub(crate) record_samples_for_structs: bool,
}
impl Default for TracerConfig {
fn default() -> Self {
Self {
is_human_readable: false,
record_samples_for_newtype_structs: true,
record_samples_for_tuple_structs: false,
record_samples_for_structs: false,
}
}
}
impl TracerConfig {
#[allow(clippy::wrong_self_convention)]
pub fn is_human_readable(mut self, value: bool) -> Self {
self.is_human_readable = value;
self
}
pub fn record_samples_for_newtype_structs(mut self, value: bool) -> Self {
self.record_samples_for_newtype_structs = value;
self
}
pub fn record_samples_for_tuple_structs(mut self, value: bool) -> Self {
self.record_samples_for_tuple_structs = value;
self
}
pub fn record_samples_for_structs(mut self, value: bool) -> Self {
self.record_samples_for_structs = value;
self
}
}
impl Tracer {
pub fn new(config: TracerConfig) -> Self {
Self {
config,
registry: BTreeMap::new(),
incomplete_enums: BTreeSet::new(),
}
}
pub fn trace_value<T>(&mut self, samples: &mut Samples, value: &T) -> Result<(Format, Value)>
where
T: ?Sized + Serialize,
{
let serializer = Serializer::new(self, samples);
let (mut format, sample) = value.serialize(serializer)?;
format.reduce();
Ok((format, sample))
}
pub fn trace_type_once<'de, T>(&mut self, samples: &'de Samples) -> Result<(Format, T)>
where
T: Deserialize<'de>,
{
let mut format = Format::unknown();
let deserializer = Deserializer::new(self, samples, &mut format);
let value = T::deserialize(deserializer)?;
format.reduce();
Ok((format, value))
}
pub fn trace_type_once_with_seed<'de, S>(
&mut self,
samples: &'de Samples,
seed: S,
) -> Result<(Format, S::Value)>
where
S: DeserializeSeed<'de>,
{
let mut format = Format::unknown();
let deserializer = Deserializer::new(self, samples, &mut format);
let value = seed.deserialize(deserializer)?;
format.reduce();
Ok((format, value))
}
pub fn trace_type<'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 let Format::TypeName(name) = &format {
if self.incomplete_enums.contains(name) {
self.incomplete_enums.remove(name);
continue;
}
}
return Ok((format, values));
}
}
pub fn trace_simple_type<'de, T>(&mut self) -> Result<(Format, Vec<T>)>
where
T: Deserialize<'de>,
{
static SAMPLES: Lazy<Samples> = Lazy::new(Samples::new);
self.trace_type(&SAMPLES)
}
pub fn trace_type_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(samples, seed.clone())?;
values.push(value);
if let Format::TypeName(name) = &format {
if self.incomplete_enums.contains(name) {
self.incomplete_enums.remove(name);
continue;
}
}
return Ok((format, values));
}
}
pub fn registry(self) -> Result<Registry> {
let mut registry = self.registry;
for (name, format) in registry.iter_mut() {
format
.normalize()
.map_err(|_| Error::UnknownFormatInContainer(name.clone()))?;
}
if self.incomplete_enums.is_empty() {
Ok(registry)
} else {
Err(Error::MissingVariants(
self.incomplete_enums.into_iter().collect(),
))
}
}
pub fn registry_unchecked(self) -> Registry {
let mut registry = self.registry;
for format in registry.values_mut() {
format.normalize().unwrap_or(());
}
registry
}
pub(crate) fn record_container(
&mut self,
samples: &mut Samples,
name: &'static str,
format: ContainerFormat,
value: Value,
record_value: bool,
) -> Result<(Format, Value)> {
self.registry.entry(name.to_string()).unify(format)?;
if record_value {
samples.values.insert(name, value.clone());
}
Ok((Format::TypeName(name.into()), value))
}
pub(crate) fn record_variant(
&mut self,
samples: &mut Samples,
name: &'static str,
variant_index: u32,
variant_name: &'static str,
variant: VariantFormat,
variant_value: Value,
) -> Result<(Format, Value)> {
let mut variants = BTreeMap::new();
variants.insert(
variant_index,
Named {
name: variant_name.into(),
value: variant,
},
);
let format = ContainerFormat::Enum(variants);
let value = Value::Variant(variant_index, Box::new(variant_value));
self.record_container(samples, name, format, value, false)
}
pub(crate) fn get_sample<'de, 'a>(
&'a self,
samples: &'de Samples,
name: &'static str,
) -> Option<(&'a ContainerFormat, &'de Value)> {
match samples.value(name) {
Some(value) => {
let format = self
.registry
.get(name)
.expect("recorded containers should have a format already");
Some((format, value))
}
None => None,
}
}
}