mod context;
mod custom;
mod error;
mod meta;
mod steps;
use std::fmt::{Debug, Display};
use std::str::FromStr;
use inflector::Inflector;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::config::{DynTypeTraits, RendererFlags};
use crate::models::{
code::{IdentPath, Module},
data::{DataTypeVariant, DataTypes, Occurs, PathData},
meta::ModuleMeta,
};
pub use self::context::{Context, ValueKey, Values};
pub use self::custom::{ValueRenderer, ValueRendererBox};
pub use self::error::Error;
pub use self::meta::MetaData;
pub use self::steps::{
DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep,
NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep,
QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep,
SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep,
WithNamespaceTraitRenderStep,
};
#[must_use]
#[derive(Debug)]
pub struct Renderer<'types> {
meta: MetaData<'types>,
steps: Vec<Box<dyn RenderStep + 'types>>,
dyn_type_traits: DynTypeTraits,
}
impl<'types> Renderer<'types> {
pub fn new(types: &'types DataTypes<'types>) -> Self {
let meta = MetaData {
types,
flags: RendererFlags::empty(),
derive: vec![IdentPath::from_ident(format_ident!("Debug"))],
dyn_type_traits: Vec::new(),
alloc_crate: format_ident!("std"),
xsd_parser_types: format_ident!("xsd_parser_types"),
};
Self {
meta,
steps: Vec::new(),
dyn_type_traits: DynTypeTraits::Auto,
}
}
pub fn with_step<X>(self, step: X) -> Self
where
X: RenderStep + 'types,
{
self.with_step_boxed(Box::new(step))
}
pub fn with_step_boxed(mut self, step: Box<dyn RenderStep + 'types>) -> Self {
self.steps.push(step);
self
}
pub fn with_default_steps(self) -> Self {
self.with_step(TypesRenderStep)
}
pub fn clear_steps(mut self) -> Self {
self.steps.clear();
self
}
pub fn flags(mut self, value: RendererFlags) -> Self {
self.meta.flags = value;
self
}
pub fn derive<I>(mut self, value: I) -> Self
where
I: IntoIterator,
I::Item: Display,
{
self.meta.derive = value
.into_iter()
.map(|x| IdentPath::from_str(&x.to_string()).expect("Invalid identifier path"))
.collect();
self
}
pub fn dyn_type_traits<I>(mut self, value: I) -> Result<Self, Error>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let traits = value
.into_iter()
.map(|x| {
let s = x.as_ref();
IdentPath::from_str(s)
})
.collect::<Result<Vec<_>, _>>()?;
self.dyn_type_traits = DynTypeTraits::Custom(traits);
Ok(self)
}
pub fn alloc_crate<S: Display>(mut self, value: S) -> Self {
self.meta.alloc_crate = format_ident!("{value}");
self
}
pub fn xsd_parser_types<S: Display>(mut self, value: S) -> Self {
self.meta.xsd_parser_types = format_ident!("{value}");
self
}
#[must_use]
pub fn finish(self) -> Module {
let mut module = Module::default();
let Self {
mut meta,
mut steps,
dyn_type_traits,
} = self;
meta.dyn_type_traits = match dyn_type_traits {
DynTypeTraits::Auto => {
let traits = meta.derive.iter().map(|x| match x.to_string().as_ref() {
"Debug" => IdentPath::from_str("::core::fmt::Debug").unwrap(),
"Hash" => IdentPath::from_str("::core::hash::Hash").unwrap(),
_ => x.clone(),
});
let as_any =
IdentPath::from_parts([meta.xsd_parser_types.clone()], format_ident!("AsAny"));
traits.chain(Some(as_any)).collect()
}
DynTypeTraits::Custom(x) => x,
};
for step in &mut steps {
step.initialize(&mut meta);
}
let mut values = Values::new();
for (ident, data) in &meta.types.items {
let mut ctx = Context::new(&meta, data, ident, &mut module, values);
for step in &mut steps {
step.render_type(&mut ctx);
}
values = ctx.values;
}
for step in &mut steps {
step.finish(&meta, &mut module);
}
module
}
}
pub trait RenderStep: Debug {
fn render_step_type(&self) -> RenderStepType;
fn initialize(&mut self, meta: &mut MetaData<'_>) {
let _meta = meta;
}
fn render_type(&mut self, ctx: &mut Context<'_, '_>) {
let _cxt = ctx;
}
fn finish(&mut self, meta: &MetaData<'_>, module: &mut Module) {
let _meta = meta;
let _module = module;
}
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
pub enum RenderStepType {
Types,
ExtraTypes,
ExtraImpls,
#[default]
Undefined,
}
impl RenderStepType {
#[must_use]
pub fn is_mutual_exclusive_to(&self, other: Self) -> bool {
matches!((self, other), (Self::Types, Self::Types))
}
}
impl ModuleMeta {
pub(super) fn make_ns_const(&self) -> Option<PathData> {
self.namespace.as_ref()?;
let name = self.name().map_or_else(
|| format!("UNNAMED_{}", self.namespace_id.0),
|name| name.as_str().to_screaming_snake_case(),
);
let ident = format_ident!("NS_{name}");
let path = IdentPath::from_parts([], ident);
Some(PathData::from_path(path))
}
pub(super) fn make_prefix_const(&self) -> Option<PathData> {
self.namespace.as_ref()?;
let name = self.name()?;
let name = name.as_str().to_screaming_snake_case();
let ident = format_ident!("PREFIX_{name}");
let path = IdentPath::from_parts([], ident);
Some(PathData::from_path(path))
}
}
impl Occurs {
#[must_use]
pub fn make_type(
self,
ctx: &Context<'_, '_>,
ident: &TokenStream,
need_indirection: bool,
) -> Option<TokenStream> {
match self {
Self::None => None,
Self::Single if need_indirection => {
let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
Some(quote! { #box_<#ident> })
}
Self::Single => Some(quote! { #ident }),
Self::Optional if need_indirection => {
let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
let option = ctx.resolve_build_in("::core::option::Option");
Some(quote! { #option<#box_<#ident>> })
}
Self::Optional => {
let option = ctx.resolve_build_in("::core::option::Option");
Some(quote! { #option<#ident> })
}
Self::DynamicList => {
let vec = ctx.resolve_build_in("::alloc::vec::Vec");
Some(quote! { #vec<#ident> })
}
Self::StaticList(sz) if need_indirection => {
let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
Some(quote! { [#box_<#ident>; #sz] })
}
Self::StaticList(sz) => Some(quote! { [#ident; #sz] }),
}
}
}