mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
145 lines
4.7 KiB
Rust
145 lines
4.7 KiB
Rust
/*!
|
|
Helpers used both for enums and structs.
|
|
*/
|
|
use proc_macro2::TokenStream;
|
|
|
|
use quote::quote;
|
|
use syn::*;
|
|
|
|
use crate::error_message::ParentRef;
|
|
|
|
/// Extract the relevant parts from an [`Item`]'s [`Generics`] so
|
|
/// that they can be used inside [`quote::quote`] to form `impl` items.
|
|
///
|
|
/// The returned parts are:
|
|
/// - The list of parameters incl. bounds enclosed in `< .. >`, for use right
|
|
/// after the `impl` keyword. If there are no parameters, this part is
|
|
/// empty.
|
|
/// - The list of parameters without bounds enclosed in `< .. >`, for use when
|
|
/// referring to the Item's type. If there are no parameters, this part is
|
|
/// empty.
|
|
/// - The where clause, if any.
|
|
///
|
|
/// The results are formed so that they can be used unconditionally, i.e. the
|
|
/// parameter lists are completely empty token streams if and only if the
|
|
/// [`Generics`] do not contain any parameters.
|
|
pub(crate) fn bake_generics(generics: Generics) -> (TokenStream, TokenStream, Option<WhereClause>) {
|
|
let params = generics.params;
|
|
let where_clause = generics.where_clause;
|
|
if params.len() > 0 {
|
|
let mut params_ref = Vec::new();
|
|
for param in params.iter() {
|
|
params_ref.push(match param {
|
|
GenericParam::Lifetime(lt) => GenericArgument::Lifetime(lt.lifetime.clone()),
|
|
GenericParam::Type(ty) => GenericArgument::Type(Type::Path(TypePath {
|
|
qself: None,
|
|
path: ty.ident.clone().into(),
|
|
})),
|
|
GenericParam::Const(cst) => GenericArgument::Const(Expr::Path(ExprPath {
|
|
attrs: Vec::new(),
|
|
qself: None,
|
|
path: cst.ident.clone().into(),
|
|
})),
|
|
});
|
|
}
|
|
(
|
|
quote! {
|
|
< #params >
|
|
},
|
|
quote! {
|
|
< #( #params_ref ),* >
|
|
},
|
|
where_clause,
|
|
)
|
|
} else {
|
|
(quote! {}, quote! {}, where_clause)
|
|
}
|
|
}
|
|
|
|
/// Build a statement calling the validator function at `validate`, if any.
|
|
///
|
|
/// This assumes that the argument for `validate` is called `result`.
|
|
pub(crate) fn build_validate(validate: Option<&Path>) -> Stmt {
|
|
syn::parse2(if let Some(validate) = validate {
|
|
quote! {
|
|
#validate(&mut result)?;
|
|
}
|
|
} else {
|
|
quote! {
|
|
{ let _ = &mut result; };
|
|
}
|
|
})
|
|
.expect("failed to build validation code")
|
|
}
|
|
|
|
/// Build a statement calling the preparation function at `prepare`, if any.
|
|
///
|
|
/// The argument passed to `prepare` is `value_ident`.
|
|
pub(crate) fn build_prepare(prepare: Option<&Path>, value_ident: &Ident) -> TokenStream {
|
|
if let Some(prepare) = prepare {
|
|
quote! {
|
|
#prepare(&mut #value_ident);
|
|
}
|
|
} else {
|
|
quote! {
|
|
{ let _ = &mut #value_ident; };
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait ItemDef: std::fmt::Debug {
|
|
/// Construct an expression which consumes `residual` and evaluates to
|
|
/// `Result<T, Error>`.
|
|
///
|
|
/// - `item_name` may contain either the path necessary to construct an
|
|
/// instance of the item or a nested parent ref. The latter case may not
|
|
/// be supported by all implementations of `ItemDef`.
|
|
///
|
|
/// - `residual` must be the identifier of the `minidom::Element` to
|
|
/// process.
|
|
fn build_try_from_element(
|
|
&self,
|
|
item_name: &ParentRef,
|
|
residual: &Ident,
|
|
) -> Result<TokenStream>;
|
|
|
|
/// Construct an expression which consumes the `T` value at `value_ident`
|
|
/// and returns a `minidom::Element`.
|
|
///
|
|
/// - `item_name` is used primarily for diagnostic messages.
|
|
///
|
|
/// - `value_ident` must be the identifier at which the entire struct can
|
|
/// be reached. It is used during preparation.
|
|
fn build_into_element(&self, item_name: &ParentRef, value_ident: &Ident)
|
|
-> Result<TokenStream>;
|
|
|
|
/// Construct a token stream containing the entire body of the
|
|
/// `impl DynNamespace` block.
|
|
///
|
|
/// Can only be used on `namespace = dyn` items; any other variants will
|
|
/// cause an appropriate compile-time error.
|
|
fn build_dyn_namespace(&self) -> Result<TokenStream>;
|
|
}
|
|
|
|
impl<T: ItemDef + ?Sized> ItemDef for Box<T> {
|
|
fn build_try_from_element(
|
|
&self,
|
|
item_name: &ParentRef,
|
|
residual: &Ident,
|
|
) -> Result<TokenStream> {
|
|
(**self).build_try_from_element(item_name, residual)
|
|
}
|
|
|
|
fn build_into_element(
|
|
&self,
|
|
item_name: &ParentRef,
|
|
value_ident: &Ident,
|
|
) -> Result<TokenStream> {
|
|
(**self).build_into_element(item_name, value_ident)
|
|
}
|
|
|
|
fn build_dyn_namespace(&self) -> Result<TokenStream> {
|
|
(**self).build_dyn_namespace()
|
|
}
|
|
}
|