Struct syn::meta::ParseNestedMeta
source · #[non_exhaustive]pub struct ParseNestedMeta<'a> {
pub path: Path,
pub input: ParseStream<'a>,
}
Expand description
Context for parsing a single property in the conventional syntax for structured attributes.
Examples
Refer to usage examples on the following two entry-points:
-
Attribute::parse_nested_meta
if you have an entireAttribute
to parse. Always use this if possible. Generally this is able to produce better error messages becauseAttribute
holds span information for all of the delimiters therein. -
syn::meta::parser
if you are implementing aproc_macro_attribute
macro and parsing the arguments to the attribute macro, i.e. the ones written in the same attribute that dispatched the macro invocation. Rustc does not pass span information for the surrounding delimiters into the attribute macro invocation in this situation, so error messages might be less precise.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. }
syntax; cannot be matched against without a wildcard ..
; and struct update syntax will not work.path: Path
§input: ParseStream<'a>
Implementations§
source§impl<'a> ParseNestedMeta<'a>
impl<'a> ParseNestedMeta<'a>
sourcepub fn value(&self) -> Result<ParseStream<'a>>
pub fn value(&self) -> Result<ParseStream<'a>>
Used when parsing key = "value"
syntax.
All it does is advance meta.input
past the =
sign in the input. You
could accomplish the same effect by writing
meta.parse::<Token![=]>()?
, so at most it is a minor convenience to
use meta.value()?
.
Example
use syn::{parse_quote, Attribute, LitStr};
let attr: Attribute = parse_quote! {
#[tea(kind = "EarlGrey")]
};
// conceptually:
if attr.path().is_ident("tea") { // this parses the `tea`
attr.parse_nested_meta(|meta| { // this parses the `(`
if meta.path.is_ident("kind") { // this parses the `kind`
let value = meta.value()?; // this parses the `=`
let s: LitStr = value.parse()?; // this parses `"EarlGrey"`
if s.value() == "EarlGrey" {
// ...
}
Ok(())
} else {
Err(meta.error("unsupported attribute"))
}
})?;
}
sourcepub fn parse_nested_meta(
&self,
logic: impl FnMut(ParseNestedMeta<'_>) -> Result<()>
) -> Result<()>
pub fn parse_nested_meta(
&self,
logic: impl FnMut(ParseNestedMeta<'_>) -> Result<()>
) -> Result<()>
Used when parsing list(...)
syntax if the content inside the
nested parentheses is also expected to conform to Rust’s structured
attribute convention.
Example
use syn::{parse_quote, Attribute};
let attr: Attribute = parse_quote! {
#[tea(with(sugar, milk))]
};
if attr.path().is_ident("tea") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("with") {
meta.parse_nested_meta(|meta| { // <---
if meta.path.is_ident("sugar") {
// Here we can go even deeper if needed.
Ok(())
} else if meta.path.is_ident("milk") {
Ok(())
} else {
Err(meta.error("unsupported ingredient"))
}
})
} else {
Err(meta.error("unsupported tea property"))
}
})?;
}
Counterexample
If you don’t need parse_nested_meta
’s help in parsing the content
written within the nested parentheses, keep in mind that you can always
just parse it yourself from the exposed ParseStream. Rust syntax permits
arbitrary tokens within those parentheses so for the crazier stuff,
parse_nested_meta
is not what you want.
use syn::{parenthesized, parse_quote, Attribute, LitInt};
let attr: Attribute = parse_quote! {
#[repr(align(32))]
};
let mut align: Option<LitInt> = None;
if attr.path().is_ident("repr") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("align") {
let content;
parenthesized!(content in meta.input);
align = Some(content.parse()?);
Ok(())
} else {
Err(meta.error("unsupported repr"))
}
})?;
}
sourcepub fn error(&self, msg: impl Display) -> Error
pub fn error(&self, msg: impl Display) -> Error
Report that the attribute’s content did not conform to expectations.
The span of the resulting error will cover meta.path
and everything
that has been parsed so far since it.
There are 2 ways you might call this. First, if meta.path
is not
something you recognize:
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("kind") {
// ...
Ok(())
} else {
Err(meta.error("unsupported tea property"))
}
})?;
In this case, it behaves exactly like
syn::Error::new_spanned(&meta.path, "message...")
.
error: unsupported tea property
--> src/main.rs:3:26
|
3 | #[tea(kind = "EarlGrey", wat = "foo")]
| ^^^
More usefully, the second place is if you’ve already parsed a value but have decided not to accept the value:
use syn::Expr;
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("kind") {
let expr: Expr = meta.value()?.parse()?;
match expr {
Expr::Lit(expr) => /* ... */
Expr::Path(expr) => /* ... */
Expr::Macro(expr) => /* ... */
_ => Err(meta.error("tea kind must be a string literal, path, or macro")),
}
} else /* as above */
})?;
error: tea kind must be a string literal, path, or macro
--> src/main.rs:3:7
|
3 | #[tea(kind = async { replicator.await })]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Often you may want to use syn::Error::new_spanned
even in this
situation. In the above code, that would be:
match expr {
Expr::Lit(expr) => /* ... */
Expr::Path(expr) => /* ... */
Expr::Macro(expr) => /* ... */
_ => Err(Error::new_spanned(expr, "unsupported expression type for `kind`")),
}
error: unsupported expression type for `kind`
--> src/main.rs:3:14
|
3 | #[tea(kind = async { replicator.await })]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^