use std::borrow::Cow; use std::cell::RefCell; use std::collections::hash_map::HashMap; use std::collections::HashSet; use std::hash::BuildHasher; use std::num; use std::rc::Rc; use std::sync::atomic::AtomicBool; use std::sync::Arc;
/// Create an instance from an item in an attribute declaration. /// /// # Implementing `FromMeta` /// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct. /// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors. /// /// # Provided Implementations /// ## bool /// /// * Word with no value specified - becomes `true`. /// * As a boolean literal, e.g. `foo = true`. /// * As a string literal, e.g. `foo = "true"`. /// /// ## char /// * As a char literal, e.g. `foo = '#'`. /// * As a string literal consisting of a single character, e.g. `foo = "#"`. /// /// ## String /// * As a string literal, e.g. `foo = "hello"`. /// * As a raw string literal, e.g. `foo = r#"hello "world""#`. /// /// ## Number /// * As a string literal, e.g. `foo = "-25"`. /// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks. /// /// ## () /// * Word with no value specified, e.g. `foo`. This is best used with `Option`. /// See `darling::util::Flag` for a more strongly-typed alternative. /// /// ## Option /// * Any format produces `Some`. /// /// ## `Result<T, darling::Error>` /// * Allows for fallible parsing; will populate the target field with the result of the /// parse attempt. pubtrait FromMeta: Sized { fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
(match *item {
NestedMeta::Lit(ref lit) => Self::from_value(lit),
NestedMeta::Meta(ref mi) => Self::from_meta(mi),
})
.map_err(|e| e.with_span(item))
}
/// Create an instance from a `syn::Meta` by dispatching to the format-appropriate /// trait function. This generally should not be overridden by implementers. /// /// # Error Spans /// If this method is overridden and can introduce errors that weren't passed up from /// other `from_meta` calls, the override must call `with_span` on the error using the /// `item` to make sure that the emitted diagnostic points to the correct location in /// source code. fn from_meta(item: &Meta) -> Result<Self> {
(match *item {
Meta::Path(_) => Self::from_word(),
Meta::List(ref value) => { Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..])
}
Meta::NameValue(ref value) => Self::from_expr(&value.value),
})
.map_err(|e| e.with_span(item))
}
/// When a field is omitted from a parent meta-item, `from_none` is used to attempt /// recovery before a missing field error is generated. /// /// **Most types should not override this method.** `darling` already allows field-level /// missing-field recovery using `#[darling(default)]` and `#[darling(default = "...")]`, /// and users who add a `String` field to their `FromMeta`-deriving struct would be surprised /// if they get back `""` instead of a missing field error when that field is omitted. /// /// The primary use-case for this is `Option<T>` fields gracefully handlling absence without /// needing `#[darling(default)]`. fn from_none() -> Option<Self> {
None
}
/// Create an instance from the presence of the word in the attribute with no /// additional options specified. fn from_word() -> Result<Self> {
Err(Error::unsupported_format("word"))
}
/// Create an instance from a list of nested meta items. #[allow(unused_variables)] fn from_list(items: &[NestedMeta]) -> Result<Self> {
Err(Error::unsupported_format("list"))
}
/// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`. /// This dispatches to the appropriate method based on the type of literal encountered, /// and generally should not be overridden by implementers. /// /// # Error Spans /// If this method is overridden, the override must make sure to add `value`'s span /// information to the returned error by calling `with_span(value)` on the `Error` instance. fn from_value(value: &Lit) -> Result<Self> {
(match *value {
Lit::Bool(ref b) => Self::from_bool(b.value),
Lit::Str(ref s) => Self::from_string(&s.value()),
Lit::Char(ref ch) => Self::from_char(ch.value()),
_ => Err(Error::unexpected_lit_type(value)),
})
.map_err(|e| e.with_span(value))
}
fn from_expr(expr: &Expr) -> Result<Self> { match *expr {
Expr::Lit(ref lit) => Self::from_value(&lit.lit),
Expr::Group(ref group) => { // syn may generate this invisible group delimiter when the input to the darling // proc macro (specifically, the attributes) are generated by a // macro_rules! (e.g. propagating a macro_rules!'s expr) // Since we want to basically ignore these invisible group delimiters, // we just propagate the call to the inner expression. Self::from_expr(&group.expr)
}
_ => Err(Error::unexpected_expr_type(expr)),
}
.map_err(|e| e.with_span(expr))
}
/// Create an instance from a char literal in a value position. #[allow(unused_variables)] fn from_char(value: char) -> Result<Self> {
Err(Error::unexpected_type("char"))
}
/// Create an instance from a string literal in a value position. #[allow(unused_variables)] fn from_string(value: &str) -> Result<Self> {
Err(Error::unexpected_type("string"))
}
/// Create an instance from a bool literal in a value position. #[allow(unused_variables)] fn from_bool(value: bool) -> Result<Self> {
Err(Error::unexpected_type("bool"))
}
}
/// Generate an impl of `FromMeta` that will accept strings which parse to numbers or /// integer literals.
macro_rules! from_meta_num {
($ty:path) => { impl FromMeta for $ty { fn from_string(s: &str) -> Result<Self> {
s.parse().map_err(|_| Error::unknown_value(s))
}
/// Generate an impl of `FromMeta` that will accept strings which parse to floats or /// float literals.
macro_rules! from_meta_float {
($ty:ident) => { impl FromMeta for $ty { fn from_string(s: &str) -> Result<Self> {
s.parse().map_err(|_| Error::unknown_value(s))
}
/// Parsing support for punctuated. This attempts to preserve span information /// when available, but also supports parsing strings with the call site as the /// emitted span. impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> { fn from_value(value: &Lit) -> Result<Self> { iflet Lit::Str(ref ident) = *value {
ident
.parse_with(syn::punctuated::Punctuated::parse_terminated)
.map_err(|_| Error::unknown_lit_str_value(ident))
} else {
Err(Error::unexpected_lit_type(value))
}
}
}
/// Support for arbitrary expressions as values in a meta item. /// /// For backwards-compatibility to versions of `darling` based on `syn` 1, /// string literals will be "unwrapped" and their contents will be parsed /// as an expression. /// /// See [`util::parse_expr`](crate::util::parse_expr) for functions to provide /// alternate parsing modes for this type. impl FromMeta for syn::Expr { fn from_expr(expr: &Expr) -> Result<Self> { match expr {
Expr::Lit(syn::ExprLit {
lit: lit @ syn::Lit::Str(_),
..
}) => Self::from_value(lit),
Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
_ => Ok(expr.clone()),
}
}
/// Parser for paths that supports both quote-wrapped and bare values. impl FromMeta for syn::Path { fn from_string(value: &str) -> Result<Self> {
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
}
fn from_expr(expr: &Expr) -> Result<Self> { match expr {
Expr::Lit(lit) => Self::from_value(&lit.lit), // All idents are paths, but not all paths are idents - // the get_ident() method does additional validation to // make sure the path is actually an ident.
Expr::Path(path) => match path.path.get_ident() {
Some(ident) => Ok(ident.clone()),
None => Err(Error::unexpected_expr_type(expr)),
},
Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
_ => Err(Error::unexpected_expr_type(expr)),
}
}
}
/// Adapter for various expression types. /// /// Prior to syn 2.0, darling supported arbitrary expressions as long as they /// were wrapped in quotation marks. This was helpful for people writing /// libraries that needed expressions, but it now creates an ambiguity when /// parsing a meta item. /// /// To address this, the macro supports both formats; if it cannot parse the /// item as an expression of the right type and the passed-in expression is /// a string literal, it will fall back to parsing the string contents.
macro_rules! from_syn_expr_type {
($ty:path, $variant:ident) => { impl FromMeta for $ty { fn from_expr(expr: &syn::Expr) -> Result<Self> { match expr {
syn::Expr::$variant(body) => Ok(body.clone()),
syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
_ => Err(Error::unexpected_expr_type(expr)),
}
}
/// Adapter from `syn::parse::Parse` to `FromMeta` for items that cannot /// be expressed in a [`syn::MetaNameValue`]. /// /// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values. /// Therefore, we use a macro and a lot of impls.
macro_rules! from_syn_parse {
($ty:path) => { impl FromMeta for $ty { fn from_string(value: &str) -> Result<Self> {
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
}
// `#[darling(flatten)]` forwards directly to this method, so it's // necessary to declare it to avoid getting an unsupported format // error if it's invoked directly. fn from_list(items: &[NestedMeta]) -> Result<Self> {
Ok(FromMeta::from_list(items))
}
/// Create an impl that forwards to an inner type `T` for parsing.
macro_rules! smart_pointer_t {
($ty:path, $map_fn:path) => { impl<T: FromMeta> FromMeta for $ty { fn from_none() -> Option<Self> {
T::from_none().map($map_fn)
}
// `#[darling(flatten)]` forwards directly to this method, so it's // necessary to declare it to avoid getting an unsupported format // error if it's invoked directly. fn from_list(items: &[NestedMeta]) -> Result<Self> {
FromMeta::from_list(items).map($map_fn)
}
/// Parses the meta-item, and in case of error preserves a copy of the input for /// later analysis. impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> { fn from_meta(item: &Meta) -> Result<Self> {
T::from_meta(item)
.map(Ok)
.or_else(|_| Ok(Err(item.clone())))
}
}
/// Trait to convert from a path into an owned key for a map. trait KeyFromPath: Sized { fn from_path(path: &syn::Path) -> Result<Self>; fn to_display(&self) -> Cow<'_, str>;
}
macro_rules! hash_map {
($key:ty) => { impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> { fn from_list(nested: &[NestedMeta]) -> Result<Self> { // Convert the nested meta items into a sequence of (path, value result) result tuples. // An outer Err means no (key, value) structured could be found, while an Err in the // second position of the tuple means that value was rejected by FromMeta. // // We defer key conversion into $key so that we don't lose span information in the case // of String keys; we'll need it for good duplicate key errors later. let pairs = nested
.iter()
.map(|item| -> Result<(&syn::Path, Result<V>)> { match *item {
NestedMeta::Meta(ref inner) => { let path = inner.path();
Ok((
path,
FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
))
}
NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
}
});
letmut errors = Error::accumulator(); // We need to track seen keys separately from the final map, since a seen key with an // Err value won't go into the final map but should trigger a duplicate field error. // // This is a set of $key rather than Path to avoid the possibility that a key type // parses two paths of different values to the same key value. letmut seen_keys = HashSet::with_capacity(nested.len());
// The map to return in the Ok case. Its size will always be exactly nested.len(), // since otherwise ≥1 field had a problem and the entire map is dropped immediately // when the function returns `Err`. letmut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
for item in pairs { iflet Some((path, value)) = errors.handle(item) { let key: $key = match KeyFromPath::from_path(path) {
Ok(k) => k,
Err(e) => {
errors.push(e);
// Surface value errors even under invalid keys
errors.handle(value);
continue;
}
};
let already_seen = seen_keys.contains(&key);
if already_seen {
errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
}
match value {
Ok(_) if already_seen => {}
Ok(val) => {
map.insert(key.clone(), val);
}
Err(e) => {
errors.push(e);
}
}
seen_keys.insert(key);
}
}
errors.finish_with(map)
}
}
};
}
// This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility // with 0.12.x, while still sharing the same impl.
hash_map!(String);
hash_map!(syn::Ident);
hash_map!(syn::Path);
/// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input, /// it should not be considered by the parsing. #[cfg(test)] mod tests { use std::num::{NonZeroU32, NonZeroU64};
use proc_macro2::TokenStream; use quote::quote; use syn::parse_quote;
usecrate::{Error, FromMeta, Result};
/// parse a string as a syn::Meta instance. fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> { let attribute: syn::Attribute = parse_quote!(#[#tokens]);
Ok(attribute.meta)
}
#[track_caller] fn fm<T: FromMeta>(tokens: TokenStream) -> T {
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
.expect("Tests should pass valid input")
}
#[test] fn hash_map_succeeds() { use std::collections::HashMap;
let comparison = { letmut c = HashMap::new();
c.insert("hello".to_string(), true);
c.insert("world".to_string(), false);
c.insert("there".to_string(), true);
c
};
assert_eq!(
fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
comparison
);
}
/// Check that a `HashMap` cannot have duplicate keys, and that the generated error /// is assigned a span to correctly target the diagnostic message. #[test] fn hash_map_duplicate() { use std::collections::HashMap;
let err: Result<HashMap<String, bool>> =
FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
let err = err.expect_err("Duplicate keys in HashMap should error");
#[test] fn hash_map_multiple_errors() { use std::collections::HashMap;
let err = HashMap::<String, bool>::from_meta(
&pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
)
.expect_err("Duplicates and bad values should error");
assert_eq!(err.len(), 3); let errors = err.into_iter().collect::<Vec<_>>();
assert!(errors[0].has_span());
assert!(errors[1].has_span());
assert!(errors[2].has_span());
}
#[test] fn hash_map_ident_succeeds() { use std::collections::HashMap; use syn::parse_quote;
let comparison = { letmut c = HashMap::<syn::Ident, bool>::new();
c.insert(parse_quote!(first), true);
c.insert(parse_quote!(second), false);
c
};
assert_eq!(
fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
comparison
);
}
#[test] fn hash_map_ident_rejects_non_idents() { use std::collections::HashMap;
let err: Result<HashMap<syn::Ident, bool>> =
FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
err.unwrap_err();
}
#[test] fn hash_map_path_succeeds() { use std::collections::HashMap; use syn::parse_quote;
let comparison = { letmut c = HashMap::<syn::Path, bool>::new();
c.insert(parse_quote!(first), true);
c.insert(parse_quote!(the::second), false);
c
};
/// Tests that fallible parsing will always produce an outer `Ok` (from `fm`), /// and will accurately preserve the inner contents. #[test] fn darling_result_succeeds() {
fm::<Result<()>>(quote!(ignore)).unwrap();
fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
}
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.