use camino::Utf8PathBuf;
use core::fmt;
use core::fmt::Formatter;
use core::marker::PhantomData;
use serde::Deserialize;
use serde::Deserializer;
#[derive(Clone, Debug)]
pub struct Config {
pub output_path: Utf8PathBuf,
pub pretty_printer: PrettyPrinterConfig,
}
impl<'de> Deserialize<'de> for Config {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Field {
OutputPath,
PrettyPrinter,
Ignored,
}
struct FieldVisitor;
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("field identifier")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
0u64 => Ok(Field::OutputPath),
1u64 => Ok(Field::PrettyPrinter),
_ => Ok(Field::Ignored),
}
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
"output_path" => Ok(Field::OutputPath),
"pretty_printer" => Ok(Field::PrettyPrinter),
_ => Ok(Field::Ignored),
}
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
b"output_path" => Ok(Field::OutputPath),
b"pretty_printer" => Ok(Field::PrettyPrinter),
_ => Ok(Field::Ignored),
}
}
}
impl<'de> Deserialize<'de> for Field {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct Visitor<'de> {
lifetime: PhantomData<&'de ()>,
}
impl<'de> serde::de::Visitor<'de> for Visitor<'de> {
type Value = Config;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("struct Config")
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let output_path = seq
.next_element::<Utf8PathBuf>()?
.ok_or_else(|| {
serde::de::Error::invalid_length(0, &"struct Config with 2 elements")
})
.and_then(|path| path.canonicalize_utf8().map_err(serde::de::Error::custom))?;
let pretty_printer = seq
.next_element::<PrettyPrinterConfig>()?
.unwrap_or_default();
Ok(Config {
output_path,
pretty_printer,
})
}
#[inline]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut output_path: Option<Utf8PathBuf> = None;
let mut pretty_printer: Option<PrettyPrinterConfig> = None;
while let Some(key) = map.next_key::<Field>()? {
match key {
Field::OutputPath if output_path.is_some() => {
return Err(serde::de::Error::duplicate_field("output_path"));
}
Field::OutputPath => {
output_path = Some(map.next_value::<Utf8PathBuf>()?);
}
Field::PrettyPrinter if pretty_printer.is_some() => {
return Err(serde::de::Error::duplicate_field("pretty_printer"));
}
Field::PrettyPrinter => {
pretty_printer = Some(map.next_value::<PrettyPrinterConfig>()?);
}
_ => {
map.next_value::<serde::de::IgnoredAny>()?;
}
}
}
let output_path = output_path
.ok_or_else(|| serde::de::Error::missing_field("output_path"))
.and_then(|path| path.canonicalize_utf8().map_err(serde::de::Error::custom))?;
let pretty_printer = pretty_printer.unwrap_or_default();
Ok(Config {
output_path,
pretty_printer,
})
}
}
const FIELDS: &[&str] = &["output_path", "pretty_printer"];
deserializer.deserialize_struct(
"Config",
FIELDS,
Visitor {
lifetime: PhantomData,
},
)
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize)]
pub enum PrettyPrinterConfig {
#[default]
GloballyDerived,
LocallyDerived,
Static(beancount_pretty_printer::Config),
}