xmlity-derive 0.0.9

Derive proc-macros for xmlity.
Documentation
mod attributes;
use std::{borrow::Cow, ops::Not};

pub use attributes::SimpleDeserializeAttributeBuilder;
mod elements;
mod none;
mod single_child_element;
mod variant;
use attributes::RecordDeserializeAttributeBuilder;
use elements::RecordDeserializeElementBuilder;
use none::{EnumVisitorBuilder, RecordDeserializeValueBuilder};
use quote::ToTokens;

use crate::{
    options::{enums, records, WithExpandedNameExt},
    DeriveError, DeriveMacro,
};

use super::builders::{DeserializeBuilder, DeserializeBuilderExt};

use crate::common::{parse_enum_variant_derive_input, parse_struct_derive_input, RecordInput};

pub struct RecordDeserializeBuilder<'a, T: Fn(syn::Expr) -> syn::Expr> {
    pub input: &'a RecordInput<'a, T>,
    pub options: &'a records::roots::DeserializeRootOpts,
}

impl<'a, T: Fn(syn::Expr) -> syn::Expr> RecordDeserializeBuilder<'a, T> {
    pub fn new(
        input: &'a RecordInput<'a, T>,
        options: &'a records::roots::DeserializeRootOpts,
    ) -> Self {
        Self { input, options }
    }
}

impl<T: Fn(syn::Expr) -> syn::Expr> DeserializeBuilder for RecordDeserializeBuilder<'_, T> {
    fn deserialize_fn_body(
        &self,
        deserializer_ident: &syn::Ident,
        deserialize_lifetime: &syn::Lifetime,
    ) -> Result<Vec<syn::Stmt>, DeriveError> {
        use records::roots::DeserializeRootOpts;
        match &self.options {
            DeserializeRootOpts::Element(opts) => RecordDeserializeElementBuilder {
                input: self.input,
                ignore_whitespace: opts.ignore_whitespace,
                ignore_comments: opts.ignore_comments,
                required_expanded_name: opts.deserialize_any_name.not().then(|| {
                    opts.expanded_name(&deserializer_ident.to_string())
                        .into_owned()
                }),
                allow_unknown_attributes: opts.allow_unknown_attributes,
                allow_unknown_children: opts.allow_unknown_children,
                children_order: opts.children_order,
                attribute_order: opts.attribute_order,
            }
            .deserialize_fn_body(deserializer_ident, deserialize_lifetime),
            DeserializeRootOpts::Attribute(opts) => {
                RecordDeserializeAttributeBuilder::new(self.input, opts)
                    .to_builder()?
                    .deserialize_fn_body(deserializer_ident, deserialize_lifetime)
            }
            DeserializeRootOpts::Value(opts) => RecordDeserializeValueBuilder {
                input: self.input,
                value: opts.value.clone(),
                ignore_whitespace: opts.ignore_whitespace,
                ignore_comments: opts.ignore_comments,
                allow_unknown_children: opts.allow_unknown,
                children_order: opts.order,
                deserialize_with: opts.deserialize_with(),
            }
            .deserialize_fn_body(deserializer_ident, deserialize_lifetime),
            DeserializeRootOpts::None => RecordDeserializeValueBuilder {
                input: self.input,
                ignore_whitespace: Default::default(),
                ignore_comments: Default::default(),
                allow_unknown_children: Default::default(),
                children_order: Default::default(),
                deserialize_with: None,
                value: None,
            }
            .deserialize_fn_body(deserializer_ident, deserialize_lifetime),
        }
    }

    fn ident(&self) -> Cow<'_, syn::Ident> {
        Cow::Borrowed(self.input.impl_for_ident.as_ref())
    }

    fn generics(&self) -> Cow<'_, syn::Generics> {
        Cow::Borrowed(self.input.generics.as_ref())
    }
}

pub struct DeriveDeserialize;

impl DeriveMacro for DeriveDeserialize {
    fn input_to_derive(ast: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, DeriveError> {
        match &ast.data {
            syn::Data::Struct(_) => {
                let opts = records::roots::DeserializeRootOpts::parse(&ast.attrs)?;

                let record = parse_struct_derive_input(ast)?;
                RecordDeserializeBuilder::new(&record, &opts)
                    .deserialize_trait_impl()
                    .map(|a| a.to_token_stream())
            }
            syn::Data::Enum(_) => {
                let opts = enums::roots::RootOpts::parse(ast)?;

                let value_opts = match &opts {
                    enums::roots::RootOpts::None => None,
                    enums::roots::RootOpts::Value(opts) => Some(opts),
                };

                EnumVisitorBuilder::new(ast, value_opts)
                    .deserialize_trait_impl()
                    .map(|a| a.to_token_stream())
            }
            syn::Data::Union(_) => Err(DeriveError::custom(
                "Unions are not supported for deserialization.",
            )),
        }
    }
}