use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use syn::ext::IdentExt; use syn::parse::Parser; use syn::spanned::Spanned; use syn::Token;
/// A struct or enum body. /// /// `V` is the type which receives any encountered variants, and `F` receives struct fields. #[derive(Debug, Clone, PartialEq, Eq)] pubenum Data<V, F> { Enum(Vec<V>), Struct(Fields<F>),
}
impl<V, F> Data<V, F> { /// Creates an empty body of the same shape as the passed-in body. /// /// # Panics /// This function will panic if passed `syn::Data::Union`. pubfn empty_from(src: &syn::Data) -> Self { match *src {
syn::Data::Enum(_) => Data::Enum(vec![]),
syn::Data::Struct(ref vd) => Data::Struct(Fields::empty_from(&vd.fields)),
syn::Data::Union(_) => panic!("Unions are not supported"),
}
}
/// Creates an empty body of the same shape as the passed-in body. /// /// `darling` does not support unions; calling this function with a union body will return an error. pubfn try_empty_from(src: &syn::Data) -> Result<Self> { match *src {
syn::Data::Enum(_) => Ok(Data::Enum(vec![])),
syn::Data::Struct(ref vd) => Ok(Data::Struct(Fields::empty_from(&vd.fields))), // This deliberately doesn't set a span on the error message, as the error is most useful if // applied to the call site of the offending macro. Given that the message is very generic, // putting it on the union keyword ends up being confusing.
syn::Data::Union(_) => Err(Error::custom("Unions are not supported")),
}
}
/// Creates a new `Data<&'a V, &'a F>` instance from `Data<V, F>`. pubfn as_ref(&self) -> Data<&V, &F> { match *self {
Data::Enum(ref variants) => Data::Enum(variants.iter().collect()),
Data::Struct(ref data) => Data::Struct(data.as_ref()),
}
}
/// Applies a function `V -> U` on enum variants, if this is an enum. pubfn map_enum_variants<T, U>(self, map: T) -> Data<U, F> where
T: FnMut(V) -> U,
{ matchself {
Data::Enum(v) => Data::Enum(v.into_iter().map(map).collect()),
Data::Struct(f) => Data::Struct(f),
}
}
/// Applies a function `F -> U` on struct fields, if this is a struct. pubfn map_struct_fields<T, U>(self, map: T) -> Data<V, U> where
T: FnMut(F) -> U,
{ matchself {
Data::Enum(v) => Data::Enum(v),
Data::Struct(f) => Data::Struct(f.map(map)),
}
}
/// Applies a function to the `Fields` if this is a struct. pubfn map_struct<T, U>(self, mut map: T) -> Data<V, U> where
T: FnMut(Fields<F>) -> Fields<U>,
{ matchself {
Data::Enum(v) => Data::Enum(v),
Data::Struct(f) => Data::Struct(map(f)),
}
}
/// Consumes the `Data`, returning `Fields<F>` if it was a struct. pubfn take_struct(self) -> Option<Fields<F>> { matchself {
Data::Enum(_) => None,
Data::Struct(f) => Some(f),
}
}
/// Consumes the `Data`, returning `Vec<V>` if it was an enum. pubfn take_enum(self) -> Option<Vec<V>> { matchself {
Data::Enum(v) => Some(v),
Data::Struct(_) => None,
}
}
/// Returns `true` if this instance is `Data::Enum`. pubfn is_enum(&self) -> bool { match *self {
Data::Enum(_) => true,
Data::Struct(_) => false,
}
}
/// Returns `true` if this instance is `Data::Struct`. pubfn is_struct(&self) -> bool {
!self.is_enum()
}
}
impl<V: FromVariant, F: FromField> Data<V, F> { /// Attempt to convert from a `syn::Data` instance. pubfn try_from(body: &syn::Data) -> Result<Self> { match *body {
syn::Data::Enum(ref data) => { letmut errors = Error::accumulator(); let items = data
.variants
.iter()
.filter_map(|v| errors.handle(FromVariant::from_variant(v)))
.collect();
errors.finish_with(Data::Enum(items))
}
syn::Data::Struct(ref data) => Ok(Data::Struct(Fields::try_from(&data.fields)?)), // This deliberately doesn't set a span on the error message, as the error is most useful if // applied to the call site of the offending macro. Given that the message is very generic, // putting it on the union keyword ends up being confusing.
syn::Data::Union(_) => Err(Error::custom("Unions are not supported")),
}
}
}
/// Equivalent to `syn::Fields`, but replaces the AST element with a generic. #[derive(Debug, Clone)] pubstruct Fields<T> { pub style: Style, pub fields: Vec<T>,
span: Option<Span>,
__nonexhaustive: (),
}
/// Splits the `Fields` into its style and fields for further processing. /// Returns an empty `Vec` for `Unit` data. pubfn split(self) -> (Style, Vec<T>) {
(self.style, self.fields)
}
/// Returns true if this variant's data makes it a newtype. pubfn is_newtype(&self) -> bool { self.style == Style::Tuple && self.len() == 1
}
/// Returns the number of fields in the structure. pubfn len(&self) -> usize { self.fields.len()
}
/// Returns `true` if the `Fields` contains no fields. pubfn is_empty(&self) -> bool { self.fields.is_empty()
}
}
impl<F: FromField> Fields<F> { pubfn try_from(fields: &syn::Fields) -> Result<Self> { letmut errors = Error::accumulator(); let items = { match &fields {
syn::Fields::Named(fields) => fields
.named
.iter()
.filter_map(|field| {
errors.handle(FromField::from_field(field).map_err(|err| { // There should always be an ident here, since this is a collection // of named fields, but `syn` doesn't prevent someone from manually // constructing an invalid collection so a guard is still warranted. iflet Some(ident) = &field.ident {
err.at(ident)
} else {
err
}
}))
})
.collect(),
syn::Fields::Unnamed(fields) => fields
.unnamed
.iter()
.filter_map(|field| errors.handle(FromField::from_field(field)))
.collect(),
syn::Fields::Unit => vec![],
}
};
impl<T: ToTokens> ToTokens for Fields<T> { fn to_tokens(&self, tokens: &mut TokenStream) { let fields = &self.fields; // An unknown Span should be `Span::call_site()`; // https://docs.rs/syn/1.0.12/syn/spanned/trait.Spanned.html#tymethod.span let span = self.span.unwrap_or_else(Span::call_site);
matchself.style {
Style::Struct => { let trailing_comma = { if fields.is_empty() {
quote!()
} else {
quote!(,)
}
};
/// Creates a new `Fields` of the specified style with the passed-in fields. fn with_fields<T, U: Into<Vec<T>>>(self, fields: U) -> Fields<T> {
Fields::new(self, fields.into())
}
}
// it is not possible to directly convert a TokenStream into syn::Fields, so you have // to convert the TokenStream into DeriveInput first and then pass the syn::Fields to // Fields::try_from. fn token_stream_to_fields(input: TokenStream) -> Fields<syn::Field> {
Fields::try_from(&{ iflet syn::Data::Struct(s) = syn::parse2::<syn::DeriveInput>(input).unwrap().data {
s.fields
} else {
panic!();
}
})
.unwrap()
}
#[test] fn test_style_eq() { // `Fields` implements `Eq` manually, so it has to be ensured, that all fields of `Fields` // implement `Eq`, this test would fail, if someone accidentally removed the Eq // implementation from `Style`. struct _AssertEq where
Style: Eq;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.