TedDriggs / darling

A Rust proc-macro attribute parser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Allow usage of `map` to handle fields that don't impl FromMeta

danieleades opened this issue · comments

i may be misunderstanding the use of the 'mapping' feature

I'm attempting to parse a PathBuf from a derive input. something like-

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = PathBuf::from)]
    path: PathBuf,
}

to parse-

#[derive(MyTrait)]
#[my_attribute(path = "some/path")]
struct MyStruct;

is this how 'map' is supposed to be used?

Same question for parsing a comma-separated list into a Vec<String>

just to be clear, the following does not compile using darling v0.20.3

use std::path::PathBuf;

use darling::FromDeriveInput;


#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = PathBuf::from)]
    path: PathBuf,
}

and neither does this

use std::path::PathBuf;

use darling::FromDeriveInput;


#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = "parse_path_from_string")]
    path: PathBuf,
}

fn parse_path_from_string(s: String) -> PathBuf {
    PathBuf::from(s)
}

The issue you're running into is what happens when the caller doesn't specify path. In that situation, darling will either use the function set in the field-level #[darling(default = ...)], or it will use that field from the struct-level default, or it will call FromMeta::from_none on the field's type - in this case, that's PathBuf, so it's looking for <PathBuf as FromMeta>::from_none and failing to find it because PathBuf doesn't implement FromMeta.

There are a couple options here:

  1. Setting a default at the field level does the trick. Note that I did run into #257 while testing this.
  2. We could add impl FromMeta for PathBuf - I don't see any reason not to do so, and that would maximally simplify your code. This is now #259, though I didn't link that commit to this issue.

Here's an example using default to prevent that from_none call being generated

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = parse_path_from_string, default)]
    path: Option<PathBuf>,
}

fn parse_path_from_string(s: String) -> Option<PathBuf> {
    Some(PathBuf::from(s))
}

Thanks for taking the time, I really appreciate it.

Wrapping the type in Option isn't exactly ideal, since the value is required and I would have to unwrap the Option myself (feels like it's neater to delegate that to darling).

#259 looks like it would solve my problem!

I have a similar issue where I need to parse a comma-separated list of strings into a vec of strings.
something like:

use darling::FromDeriveInput;


#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = parse_strings)]
    strings: Vec<String>,
}

fn parse_strings(s: String) -> Vec<String> {
    todo!()
}

this doesn't work either, i'm guessing for similar reasons. Is there a way to make this work without wrapping the Vec<String> in an Option?

this seems to work, and i think it meets my use-case:

use darling::FromDeriveInput;


#[derive(Debug, FromDeriveInput)]
#[darling(attributes(my_attribute))]
struct Args {
    #[darling(map = parse_strings, default)]
    strings: Vec<String>,
}

fn parse_strings(s: String) -> Vec<String> {
    vec![]
}

though i admit i don't fully understand why adding default is the secret sauce

It's because of this line, which governs how darling will handle the field if it's missing from the macro input. The from_none method allows types to override what value their absence entails; this is how Option doesn't produce an error, even if you didn't put #[darling(default)] on the field.

Having a default expression, whether explicit or implicit, causes that piece not to be emitted because we will always have a value for the field.

I deliberately don't want the from_none call to be made on the type that's passed into the map function, since that creates a lot of double-inference problems that decrease overall usability. The downside is that it soft-requires every field to impl FromMeta.

I suppose one alternative would be to allow opting out of the from_none call, saying "go directly to the error case." I'd need to think about how to name such a property though, since dont_try_to_recover_when_missing_this_field_is_not_frommeta is a bit wordy.