mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
195 lines
6.4 KiB
Rust
195 lines
6.4 KiB
Rust
/*!
|
|
# Macros for parsing XML into Rust structs, and vice versa
|
|
|
|
**If you are a user of `xso_proc` or `xso`, please
|
|
return to `xso` for more information**. The documentation of
|
|
`xso_proc` is geared toward developers of `…_macros` and `…_core`.
|
|
|
|
**You have been warned.**
|
|
|
|
## How the derive macros work
|
|
|
|
The processing is roughly grouped in the following stages:
|
|
|
|
1. [`syn`] is used to parse the incoming [`TokenStream`] into a [`syn::Item`].
|
|
Based on that, the decision is made whether a struct or an enum is being
|
|
derived on.
|
|
|
|
2. Depending on the item type (enum vs. struct), a [`ItemDef`] object is
|
|
created which implements that item. The actual implementations reside in
|
|
[`crate::structs`] and [`crate::enums`] (respectively).
|
|
|
|
1. The [`crate::meta::XmlCompoundMeta`] type is used to convert the
|
|
raw token streams from the `#[xml(..)]` attributes into structs/enums
|
|
for easier handling.
|
|
|
|
That stage only does syntax checks, no (or just little) semantics. This
|
|
separation of concerns helps with simplifying the code both in `meta`
|
|
and the following modules.
|
|
|
|
2. Enum variants and structs are processed using
|
|
[`crate::compound::Compound`], their fields being converted from
|
|
[`crate::meta::XmlFieldMeta`] to [`crate::field::FieldDef`]. For enums,
|
|
additional processing on the enum itself takes place in
|
|
[`crate::enums`]. Likewise there's special handling for structs in
|
|
[`crate::structs`].
|
|
|
|
3. If any wrapping was declared, the resulting `ItemDef` is wrapped using
|
|
[`crate::wrapped`].
|
|
|
|
3. After all data has been structured, action is taken depending on the
|
|
specific derive macro which has been invoked.
|
|
*/
|
|
#![warn(missing_docs)]
|
|
#![allow(rustdoc::private_intra_doc_links)]
|
|
mod common;
|
|
mod compound;
|
|
mod enums;
|
|
mod error_message;
|
|
mod field;
|
|
mod meta;
|
|
mod structs;
|
|
mod wrapped;
|
|
|
|
use proc_macro::TokenStream as RawTokenStream;
|
|
use proc_macro2::{Span, TokenStream};
|
|
|
|
use quote::quote;
|
|
use syn::*;
|
|
|
|
use self::common::bake_generics;
|
|
use self::common::ItemDef;
|
|
|
|
/// Parse any implemented [`syn::Item`] into a [`ItemDef`] object.
|
|
fn parse(
|
|
item: Item,
|
|
) -> Result<(
|
|
Box<dyn ItemDef>,
|
|
Ident,
|
|
TokenStream,
|
|
TokenStream,
|
|
Option<WhereClause>,
|
|
)> {
|
|
match item {
|
|
Item::Struct(item) => {
|
|
let def = self::structs::parse_struct(&item)?;
|
|
let ident = item.ident;
|
|
let (generics_decl, generics_ref, where_clause) = bake_generics(item.generics);
|
|
Ok((def, ident, generics_decl, generics_ref, where_clause))
|
|
}
|
|
Item::Enum(item) => {
|
|
let def = self::enums::parse_enum(&item)?;
|
|
let ident = item.ident;
|
|
let (generics_decl, generics_ref, where_clause) = bake_generics(item.generics);
|
|
Ok((def, ident, generics_decl, generics_ref, where_clause))
|
|
}
|
|
other => Err(Error::new_spanned(
|
|
other,
|
|
"can only be applied to enum and struct definitions",
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// Build the FromXml implementation for a given [`syn::Item`].
|
|
fn try_from_element_impl(item: Item) -> Result<TokenStream> {
|
|
let (def, ident, generics_decl, generics_ref, where_clause) = parse(item)?;
|
|
|
|
let try_from_impl = def.build_try_from_element(
|
|
&(Path::from(ident.clone()).into()),
|
|
&Ident::new("residual", Span::call_site()),
|
|
)?;
|
|
|
|
Ok(quote! {
|
|
#[allow(non_snake_case)]
|
|
impl #generics_decl ::std::convert::TryFrom<::xso::exports::minidom::Element> for #ident #generics_ref #where_clause {
|
|
type Error = ::xso::error::Error;
|
|
|
|
fn try_from(mut residual: ::xso::exports::minidom::Element) -> Result<Self, Self::Error> {
|
|
#try_from_impl
|
|
}
|
|
}
|
|
|
|
impl #generics_decl ::xso::FromXml for #ident #generics_ref #where_clause {
|
|
fn from_tree(elem: ::xso::exports::minidom::Element) -> Result<Self, ::xso::error::Error> {
|
|
Self::try_from(elem)
|
|
}
|
|
|
|
fn absent() -> Option<Self> {
|
|
None
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Derive macro for `FromXml`.
|
|
#[proc_macro_derive(FromXml, attributes(xml))]
|
|
pub fn try_from_element(input: RawTokenStream) -> RawTokenStream {
|
|
let item = syn::parse_macro_input!(input as Item);
|
|
let result = try_from_element_impl(item);
|
|
match result {
|
|
Ok(v) => v.into(),
|
|
Err(e) => e.into_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
/// Build the DynNamespace implementation for a given [`syn::Item`].
|
|
fn dyn_namespace_impl(item: Item) -> Result<TokenStream> {
|
|
let (def, ident, generics_decl, generics_ref, where_clause) = parse(item)?;
|
|
|
|
let dyn_namespace_impl = def.build_dyn_namespace()?;
|
|
|
|
Ok(quote! {
|
|
#[allow(non_snake_case)]
|
|
impl #generics_decl ::xso::DynNamespace for #ident #generics_ref #where_clause {
|
|
#dyn_namespace_impl
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Derive macro for `DynNamespace`.
|
|
#[proc_macro_derive(DynNamespace, attributes(xml))]
|
|
pub fn dyn_namespace(input: RawTokenStream) -> RawTokenStream {
|
|
let item = syn::parse_macro_input!(input as Item);
|
|
let result = dyn_namespace_impl(item);
|
|
match result {
|
|
Ok(v) => v.into(),
|
|
Err(e) => e.into_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
/// Build the IntoXml implementation for a given [`syn::Item`].
|
|
fn into_element_impl(item: Item) -> Result<TokenStream> {
|
|
let (def, ident, generics_decl, generics_ref, where_clause) = parse(item)?;
|
|
|
|
let into_element_impl = def.build_into_element(
|
|
&(Path::from(ident.clone()).into()),
|
|
&Ident::new("other", Span::call_site()),
|
|
)?;
|
|
|
|
Ok(quote! {
|
|
#[allow(non_snake_case)]
|
|
impl #generics_decl ::std::convert::From<#ident #generics_ref> for ::xso::exports::minidom::Element #where_clause {
|
|
fn from(mut other: #ident #generics_ref) -> Self {
|
|
#into_element_impl
|
|
}
|
|
}
|
|
|
|
impl #generics_decl ::xso::IntoXml for #ident #generics_ref {
|
|
fn into_tree(self) -> Option<::xso::exports::minidom::Element> {
|
|
Some(::minidom::Element::from(self))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Derive macro for `IntoXml`.
|
|
#[proc_macro_derive(IntoXml, attributes(xml))]
|
|
pub fn into_element(input: RawTokenStream) -> RawTokenStream {
|
|
let item = syn::parse_macro_input!(input as Item);
|
|
let result = into_element_impl(item);
|
|
match result {
|
|
Ok(v) => v.into(),
|
|
Err(e) => e.into_compile_error().into(),
|
|
}
|
|
}
|