Git Product home page Git Product logo

darling's Introduction

Darling

Build Status Latest Version Rustc Version 1.56+

darling is a crate for proc macro authors, which enables parsing attributes into structs. It is heavily inspired by serde both in its internals and in its API.

Benefits

  • Easy and declarative parsing of macro input - make your proc-macros highly controllable with minimal time investment.
  • Great validation and errors, no work required. When users of your proc-macro make a mistake, darling makes sure they get error markers at the right place in their source, and provides "did you mean" suggestions for misspelled fields.

Usage

darling provides a set of traits which can be derived or manually implemented.

  1. FromMeta is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like FromStr or serde::Deserialize. Trait implementations are provided for primitives, some std types, and some syn types.
  2. FromDeriveInput is implemented or derived by each proc-macro crate which depends on darling. This is the root for input parsing; it gets access to the identity, generics, and visibility of the target type, and can specify which attribute names should be parsed or forwarded from the input AST.
  3. FromField is implemented or derived by each proc-macro crate which depends on darling. Structs deriving this trait will get access to the identity (if it exists), type, and visibility of the field.
  4. FromVariant is implemented or derived by each proc-macro crate which depends on darling. Structs deriving this trait will get access to the identity and contents of the variant, which can be transformed the same as any other darling input.
  5. FromAttributes is a lower-level version of the more-specific FromDeriveInput, FromField, and FromVariant traits. Structs deriving this trait get a meta-item extractor and error collection which works for any syntax element, including traits, trait items, and functions. This is useful for non-derive proc macros.

Additional Modules

  • darling::ast provides generic types for representing the AST.
  • darling::usage provides traits and functions for determining where type parameters and lifetimes are used in a struct or enum.
  • darling::util provides helper types with special FromMeta implementations, such as PathList.

Example

use darling::{FromDeriveInput, FromMeta};

#[derive(Default, FromMeta)]
#[darling(default)]
pub struct Lorem {
    #[darling(rename = "sit")]
    ipsum: bool,
    dolor: Option<String>,
}

#[derive(FromDeriveInput)]
#[darling(attributes(my_crate), forward_attrs(allow, doc, cfg))]
pub struct MyTraitOpts {
    ident: syn::Ident,
    attrs: Vec<syn::Attribute>,
    lorem: Lorem,
}

The above code will then be able to parse this input:

/// A doc comment which will be available in `MyTraitOpts::attrs`.
#[derive(MyTrait)]
#[my_crate(lorem(dolor = "Hello", sit))]
pub struct ConsumingType;

Attribute Macros

Non-derive attribute macros are supported. To parse arguments for attribute macros, derive FromMeta on the argument receiver type, then use darling::ast::NestedMeta::parse_meta_list to convert the arguments TokenStream to a Vec<NestedMeta>, then pass that to the derived from_list method on your argument receiver type. This will produce a normal darling::Result<T> that can be used the same as a result from parsing a DeriveInput.

Macro Code

use darling::{Error, FromMeta};
use darling::ast::NestedMeta;
use syn::ItemFn;
use proc_macro::TokenStream;

#[derive(Debug, FromMeta)]
struct MacroArgs {
    #[darling(default)]
    timeout_ms: Option<u16>,
    path: String,
}

#[proc_macro_attribute]
pub fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream {
    let attr_args = match NestedMeta::parse_meta_list(args.into()) {
        Ok(v) => v,
        Err(e) => { return TokenStream::from(Error::from(e).write_errors()); }
    };
    let _input = syn::parse_macro_input!(input as ItemFn);

    let _args = match MacroArgs::from_list(&attr_args) {
        Ok(v) => v,
        Err(e) => { return TokenStream::from(e.write_errors()); }
    };

    // do things with `args`
    unimplemented!()
}

Consuming Code

use your_crate::your_attr;

#[your_attr(path = "hello", timeout_ms = 15)]
fn do_stuff() {
    println!("Hello");
}

Features

Darling's features are built to work well for real-world projects.

  • Defaults: Supports struct- and field-level defaults, using the same path syntax as serde. Additionally, Option<T> and darling::util::Flag fields are innately optional; you don't need to declare #[darling(default)] for those.
  • Field Renaming: Fields can have different names in usage vs. the backing code.
  • Auto-populated fields: Structs deriving FromDeriveInput and FromField can declare properties named ident, vis, ty, attrs, and generics to automatically get copies of the matching values from the input AST. FromDeriveInput additionally exposes data to get access to the body of the deriving type, and FromVariant exposes fields.
    • Transformation of forwarded attributes: You can add #[darling(with=path)] to the attrs field to use a custom function to transform the forwarded attributes before they're provided to your struct. The function signature is fn(Vec<Attribute>) -> darling::Result<T>, where T is the type you declared for the attrs field. Returning an error from this function will propagate with all other parsing errors.
  • Mapping function: Use #[darling(map="path")] or #[darling(and_then="path")] to specify a function that runs on the result of parsing a meta-item field. This can change the return type, which enables you to parse to an intermediate form and convert that to the type you need in your struct.
  • Skip fields: Use #[darling(skip)] to mark a field that shouldn't be read from attribute meta-items.
  • Multiple-occurrence fields: Use #[darling(multiple)] on a Vec field to allow that field to appear multiple times in the meta-item. Each occurrence will be pushed into the Vec.
  • Span access: Use darling::util::SpannedValue in a struct to get access to that meta item's source code span. This can be used to emit warnings that point at a specific field from your proc macro. In addition, you can use darling::Error::write_errors to automatically get precise error location details in most cases.
  • "Did you mean" suggestions: Compile errors from derived darling trait impls include suggestions for misspelled fields.
  • Struct flattening: Use #[darling(flatten)] to remove one level of structure when presenting your meta item to users. Fields that are not known to the parent struct will be forwarded to the flatten field.

Shape Validation

Some proc-macros only work on structs, while others need enums whose variants are either unit or newtype variants. Darling makes this sort of validation extremely simple. On the receiver that derives FromDeriveInput, add #[darling(supports(...))] and then list the shapes that your macro should accept.

Name Description
any Accept anything
struct_any Accept any struct
struct_named Accept structs with named fields, e.g. struct Example { field: String }
struct_newtype Accept newtype structs, e.g. struct Example(String)
struct_tuple Accept tuple structs, e.g. struct Example(String, String)
struct_unit Accept unit structs, e.g. struct Example;
enum_any Accept any enum
enum_named Accept enum variants with named fields
enum_newtype Accept newtype enum variants
enum_tuple Accept tuple enum variants
enum_unit Accept unit enum variants

Each one is additive, so listing #[darling(supports(struct_any, enum_newtype))] would accept all structs and any enum where every variant is a newtype variant.

This can also be used when deriving FromVariant, without the enum_ prefix.

darling's People

Contributors

dcnick3 avatar decathorpe avatar eijebong avatar gnomeddev avatar greyblake avatar hcpl avatar ignatenkobrain avatar ijackson avatar jhoobergs avatar jonasbb avatar kangalio avatar kdy1 avatar kuviman avatar leighmcculloch avatar luro02 avatar mathstuf avatar mgeisler avatar mpapierski avatar okryvyts avatar robinkrahl avatar sharksforarms avatar smoelius avatar sreenathkrishnan avatar takaakifuruse avatar teddriggs avatar ten0 avatar tenuous-guidance avatar turnofacard avatar tversteeg avatar upsuper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

darling's Issues

Change provided FromMeta impl for HashMap to collect errors

As of v0.2.0, darling's default stance on errors is to accumulate all of them before returning to the caller. This is done based on the assumption that end users of the crate would rather get all errors at once than having to recompile multiple times.

The built-in impl<T: FromMeta> FromMeta for HashMap<String, T> doesn't use the new error philosophy yet.

Optional fields in FromMeta impl derive parsing

Is there a way to have optional fields in a struct be optional when parsing? In my example,

#[derive(Debug, FromMeta)]
struct Attrs {
    path: String,
    data: Option<String>,
}
let attrs = Attrs::from_meta(&meta)?;

// parses correctly with
#[my_macro(path = "../../", data = "something")]
// But fails to parse with 
#[my_macro(path = "../../")]

Parsing alternative syntax in derived impl of `FromMeta` for enum

I'm trying to use darling for an attr macro.

Currently, given these definitions:

#[derive(Debug, FromMeta)]
enum Name {
    Fn,          // Use function name.
    Crate,       // Use crate name.
    Str(String), // Explicitly specify a name.
}

#[derive(Debug, FromMeta)]
struct Package {
    name: Name,
}

It can parse the cases below:

#[pkg(name = "fn")]
#[pkg(name = "crate")]
#[pkg(name(str = "custom-name"))]

Is it possible to configure the derived impl of FromMeta to parse the following cases instead?

#[pkg(name(fn))]             // Fn(bool) works but then name(fn = false) is valid.
#[pkg(name(crate)]
#[pkg(name = "custom-name")] // Since there's only 1 variant with 1 String field.
#[pkg(name("custom-name")]   // Same with the above.

Add sleep to publish.sh

On March 8, crates.io made index updates async (source). This breaks scripts like publish.sh. The solution is to add a 3-second sleep between steps.

Enabling proc-macro2/nightly breaks this crate

error[E0433]: failed to resolve. Could not find `darling` in `{{root}}`
 --> macros\ast_node\src\spanned.rs:3:17
  |
3 | #[derive(Debug, FromMetaItem)]
  |                 ^^^^^^^^^^^^ Could not find `darling` in `{{root}}`

This is a known issue. dtolnay/proc-macro2#67

quote! use Span::def_site() by default, which is internally Span::call_site() on stable.

Rename FromMetaItem to FromMeta

syn renamed MetaItem to Meta sometime after darling first released. Since darling is closely aligned with syn, we should probably rename the trait so that new users of the crate don't have to deal with this misalignment. However, this is a pretty big change: FromMetaItem is the foundational trait of the entire library.

Options

  1. Do a hard rename of the trait. This is simplest to implement, and shouldn't impact any of the dependent crates since we'd bump the version to 0.6.0
  2. Add the new trait and deprecate the old trait in v0.5.1, then cut over in v0.6.0. Without a blanket impl, this could split the (nonexistent) ecosystem, but it's hard to figure out which direction the blanket impl should go.

I'm leaning towards option 1, as @udoprog suggested. The directionality of the blanket impl makes it hard to pick a right transition path.

Can From* derivations point to meta item's span in error cases?

If I understand some of the proc-macro2 goals correctly, darling should be able to pinpoint the offending term in the input as the span, rather than the name of the macro.

Open Questions

  1. Can this be done on stable yet? No. See this
  2. Does this require changing the darling::Error interface? Probably. We'd want that interface to support taking an ident or span.
  3. Can darling surface multiple errors from a single invocation?

Add WithSpan<T>

In preparation for stabilization of proc-macro diagnostics, darling should offer a way to get the span for a field so that a macro author can emit targeted diagnostics, and shouldn't require the author to manually implement parsing for that syntax node. A smart pointer implementing Deref would provide a good user experience for this.

Add more `FromMetaItem` impls

Once specialization lands, it should be possible to add a blanket impl<T: FromStr> FromMetaItem for T, but in the meantime it would be useful to have better coverage of the standard library.

Syn 0.12

The crate currently does not work with the new syn 0.12

Expose high-level functions for derivation from darling_core

Most of darling_core::options and darling_core::codegen don't need to be public. Having too broad a public API surface means breaking changes that could be avoided.

Change

Expose a new function in darling_core for each derivable trait. That should the correct syn element as input and return a proc_macro2::TokenStream. This avoids darling_core depending on proc_macro directly.

Please yank 0.3.3

The 0.3.3 release breaks the signature of many methods in darling's public API by changing from syn 0.12 types in darling 0.3.2 to syn 0.13 types in darling 0.3.3. Please yank 0.3.3 and publish those changes as 0.4 instead.

Upgrade syn and quote to 1.0

In the mean time, I think darling can also re-export these dependencies, so that downstream crates can upgrade without being blocked.

Unnamed attributes

#[derive(Foo)]
#[response(Bar)]
struct Baz;

Can this currently be expressed in darling? I tired to do the following:

#[derive(FromDeriveInput)]
#[darling(attributes(response))]
struct RequestAttrs(Ident);
    |
    | #[derive(FromDeriveInput)]
    |          ^^^^^^^^^^^^^^^ the trait `darling_core::from_derive_input::FromDeriveInput` is not implemented for `proc_macro2::Ident`
    |
    = note: required by `darling_core::from_derive_input::FromDeriveInput::from_derive_input`

Improve support for non-derive attribute macros

This could be something of an open-ended investigation, but darling is currently geared towards using FromDeriveInput as its root entry point, so it's not seamless to use the parser for attributes that are not part of custom derives.

Use late error returns in darling's own attribute parsing

darling can't use its own macros to generate the types that will handle codegen for user input. The current implementation eagerly returns on errors, which means intermediate authors will get the old experience of only seeing one error at a time.

Support ignoring unknown fields in meta items

This is motivated by dtolnay/request-for-implementation#4, but applies to any crate which is trying to parse an attribute it doesn't exclusively own. In that case, there may be fields that are irrelevant to the macro's operation, and they should be ignored.

Proposal

Add a new allow_unknown_fields option to the derivations for all darling traits. This would cause darling to skip, rather than error on, unknown fields.

Open Questions

Should it be called allow_unknown_fields or ignore_unknown_fields?

Finalize error strategy

Considerations

  1. What wrapper affordance should be provided for value validation errors?
  2. Should the library return structured errors, or focus on strings?
  3. How is "location" (struct and field) conveyed in the error?

Add duplicate-field checking to darling_core for fields

Right now, the following is valid:

#[derive(FromMeta)]
pub struct Foo {
    #[darling(skip, skip = false)]
    my_field: String;
}

This is probably the result of an error in the crate that's consuming darling, so it'd be nice to tell them. However, it's worth noting that this would be a breaking change, so it needs to wait for a new minor version.

Update version on crates.io

It seems that the version on crates.io is still at 0.6.3 which does not work with syn 0.14 and quote 0.6. Would it be possible to push a 0.7 on crates.io?
(Related to issue #38)

Support derivation on generic types

darling should support any of its traits being derived on generic types.

Like the built-in derive offerings, darling should automatically bound its trait implementations rather than requiring the base type to have those bounds.

Add adapter newtype or macro option to work with syn::Parse

The syn::parse_macro_input! macro and synattra crate both use the syn::Parse trait to do their work. Anything that implements one of darling's traits should be able to implement that. A blanket impl is probably impossible, so the choices are:

  1. Provide a generic newtype wrapper and implement it on that.
  2. Add a new option to the code generator to also implement the Parse trait.
  3. Do both.

Struct enum variants should parse all fields before returning errors

Consider the following code:

#[derive(Debug, FromMetaItem)]
#[darling(rename_all="snake_case")]
enum Week {
    Monday,
    Tuesday {
        morning: bool,
        afternoon: String,
    }
}

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(accrue))]
struct Month {
    schedule: Week
}

#[accrue(schedule(tuesday(morning = "yes")))]
pub struct NonConforming {
    foo: (),
    bar: bool,
}

This should report two errors after #5 was merged:

  1. Unknown literal value yes at schedule/morning
  2. Missing value for afternoon at schedule

Instead, it is only reporting the first error.

Consider automatically making enum variants snake_case

It is idiomatic for attributes to take snake-case idents when outside literals. At the moment, this has to be added to every enum by the author, which has two major downsides:

  1. The likely-desired behavior is more verbose to achieve
  2. Forgetting to set this will cause a bad experience for downstream consumers

It should be possible to override this behavior by specifying rename_all="PascalCase" or rename_all="none"

Magic field `generics` should support user-defined types

PR #30 by @upsuper added FromTypeParam, which enables extraction of attributes on type parameters. Ideally, darling would allow a consuming crate to have that extraction done automatically into the master struct implementing FromDeriveInput, getting back to the promise of one-line parsing.

  • Add FromGenerics trait
  • impl<T: FromTypeParam> FromGenerics for Vec<T>
  • Decide if there needs to be an ast::Generics struct
  • Decide if ast::Generics needs to preserve lifetime and const GenericParam values, and if they need to be preserved in order.

@upsuper I'm working on this, but I'd love your input on the decisions listed above.

Find way to create runnable examples

Proc macros can't be consumed in the crate that declares them, which means any runnable example would require two crates (and 2+ source files). That's not practical, nor is it particularly useful for someone who's trying to understand how to use the library.

Relates to #17

Create UsesTypeParams trait

darling and other crates in the proc-macro space need to determine which type parameters are used by which fields:

  1. darling should not add a FromMeta bound on type parameters which are used only in skipped fields
  2. derive_builder should not add a Clone bound on type parameters which are used in fields where no setter is avaiable
  3. serde should not emit Serialize or Deserialize bounds on fields which are skipped in those respective tasks

I vaguely remember @dtolnay mentioning that this was a hard problem for serde to solve, so it'd be really nice for darling to provide tools and a model that other crates can pick up.

Prior Art

serde's implementation has a function with_bound that looks relevant to this problem.

Proposal

pub trait UsesTypeParams {
    /// Search `self` for `sought_types`, returning a set of refs to type parameter idents found
    /// in this syntax element.
    fn uses_type_params<'a>(&self, sought_types: &'a HashSet<Ident>) -> HashSet<&'a Ident>;
}

This trait would be implemented for syn::Data and all its children.

Usage

The caller would need to first invoke the trait over all non-skipped fields and variants, then would need to figure out if any of the "used" type parameters reference indirectly use type parameters via bounds or where clauses. All used (reachable) type parameters would then have the bound added by the caller in the emitted code.

Add enum support

Proposal

An enum would be treated the same way serde treats externally tagged enums, with an eye towards supporting: #[my_lib(foo("1.2.0.0"))]. It should be possible to support all enum variants within this framework.

Tests for darling-core do not compile

cargo build --tests fails for darling-core with rustc 1.37.0:

$ cargo build --tests
   Compiling darling_core v0.10.0 (/home/robin/darling/core)
error[E0425]: cannot find value `idents` in this scope
  --> core/src/util/path_list.rs:85:13
   |
85 |             idents.to_strings(),
   |             ^^^^^^ not found in this scope

warning: trait objects without an explicit `dyn` are deprecated
   --> core/src/error/mod.rs:382:32
    |
382 |     fn cause(&self) -> Option<&StdError> {
    |                                ^^^^^^^^ help: use `dyn`: `dyn StdError`
    |
    = note: #[warn(bare_trait_objects)] on by default

error[E0599]: no method named `ok_or` found for type `std::result::Result<syn::Meta, syn::Error>` in the current scope
   --> core/src/from_meta.rs:390:32
    |
390 |         attribute.parse_meta().ok_or("Unable to parse".into())
    |                                ^^^^^

warning: use of deprecated item 'core::str::<impl str>::trim_left_matches': superseded by `trim_start_matches`
   --> core/src/options/shape.rs:137:20
    |
137 |         match word.trim_left_matches(self.prefix) {
    |                    ^^^^^^^^^^^^^^^^^ help: replace the use of the deprecated item: `trim_start_matches`
    |
    = note: #[warn(deprecated)] on by default

warning: use of deprecated item 'core::str::<impl str>::trim_right_matches': superseded by `trim_end_matches`
   --> core/src/options/shape.rs:191:34
    |
191 |             let ty = self.prefix.trim_right_matches('_');
    |                                  ^^^^^^^^^^^^^^^^^^ help: replace the use of the deprecated item: `trim_end_matches`

error[E0599]: no method named `ok_or` found for type `std::result::Result<syn::Meta, syn::Error>` in the current scope
  --> core/src/util/path_list.rs:73:32
   |
73 |         attribute.parse_meta().ok_or("Unable to parse".into())
   |                                ^^^^^

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0425, E0599.
For more information about an error, try `rustc --explain E0425`.
error: Could not compile `darling_core`.

To learn more, run the command again with --verbose.

Add #[darling(word)] for enum variants

DefaultExpression is one example of an enum that wants to map a specific variant to the appearance of the field ident with no associated value. This would only be valid on variants in FromMeta. The variant must be a unit variant.

Add FromTypeParam

TypeParam may also have attributes, so it would be good to have a trait for that as well.

Add optional "did you mean" messages to emitted errors

When rustc errors due to a non-existent method, it sometimes provides suggestions about similarly-named APIs that the programmer might have been trying to use. It would be really nice for darling to provide that in proc-macros, especially since there's no autocomplete support for proc-macro attributes.

Some considerations:

  1. Not every type has name-value pairs that'll make sense, and we can't burden manual implementers of the darling traits with new required methods. We'll need to find a way that people can opt-in that still works with existing code.
  2. This can't bloat the generated code too much.
  3. This can't slow down code generation on the happy path.
  4. Ideally, manual trait implementations can enable this with minimal code on their end.
  5. This shouldn't be confused with other help text that we may want to allow in darling::Error.

Detailed Examples

I love where this project is going and I would be surprised if it wasn't already on your radar but a good examples directory would be excellent.

The main issue I faced yesterday when trying to use darling was getting the example in the Readme compiling I had to implement a trait by hand in order for it to work which wasn't in the example.

Once I got that working it is a bit of work to figure out how to setup something in a struct to get be able to derive data about the fields on a struct. Similar to how serde allows you to annotate each field. I think a few good examples would go a long way to making darling easily usable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.