1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
/*!
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()
}
}