Closed loganmzz closed 2 months ago
I understand the desire and some of the use-cases for this, but I don't think this is possible under darling
's current architecture.
!my_feature
isn't a valid syn::Meta
. darling
recursively transforms each item it encounters into a syn::Meta
before calling either the with
function or the FromMeta::from_meta
of the child, effectively separating the parsing operation into two phases:
TokenStream
to syn::Meta
syn::Meta
into whatever output type is expected by the receiver.Step 2 in that process is very flexible, but step 1 is very inflexible; the only way to change step 1 parsing would be to override the darling
impl of the parent element so that it used a custom parser, and then you'd have to rebuild the entire step 2 manually because your output of step 1 wouldn't match the expected input of step 2.
Even some of the deeper discussions about changing darling
's traits to allow for more bespoke syntaxes don't seem likely to support this use-case generically, as those still draw a distinction between the left-hand side of the meta item and the right-hand side.
If you're trying to specifically parse a cfg-like attribute, you might be able to do the following.
use darling::{FromDeriveInput, FromMeta};
use proc_macro2::TokenStream;
use syn::{parse_quote, Meta};
#[derive(Debug)]
struct NegatableIdentList {
tokens: TokenStream,
}
impl FromMeta for NegatableIdentList {
fn from_meta(item: &Meta) -> darling::Result<Self> {
match item {
Meta::Path(_) => Err(darling::Error::unsupported_format("word").with_span(item)),
Meta::List(list) => Ok(Self {
// A real implementation of this parse the token stream now to create the desired
// output data type. This involves a bunch of use-case-specific decisions about what
// features are needed, such as and/or/etc.
tokens: list.tokens.clone(),
}),
Meta::NameValue(_) => {
Err(darling::Error::unsupported_format("name-value").with_span(item))
}
}
}
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(hello))]
struct Demo {
inner: NegatableIdentList,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let di: syn::DeriveInput = parse_quote! {
// We need the extra `inner` here so that we don't have to reimplement the outer
// behavior of `FromDeriveInput` by hand. An alternate approach would be to forward
// the attribute that will contain this features list and then handle it using `with = ...`
// on the `attrs` magic field.
#[hello(inner(!example::flag, my_feature))]
struct Example;
};
let demo = Demo::from_derive_input(&di)?;
dbg!(&demo.inner.tokens);
Ok(())
}
In this example, you take over all parsing of the meta contents before the parsing to NestedMeta
takes place in the default impl of FromMeta
. You would then need to manually implement parsing of this item, likely using syn::parse::Parse
(though you don't have to if you didn't want to).
In most configuration systems, a "key" when present let's enable (or enforce) associated feature activation. It is currently supported by
Flag
type.However, such systems also support disabling feature.
Example syntax:
Example (additional) API: