Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
danieleades opened this issue Nov 15, 2023 · 5 comments
Open

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

danieleades opened this issue Nov 15, 2023 · 5 comments

Comments

@danieleades
Copy link

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>

@danieleades
Copy link
Author

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)
}

@TedDriggs
Copy link
Owner

TedDriggs commented Nov 17, 2023

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 Field-level default should take path without quotation marks #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 impl FromMeta for PathBuf #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))
}

@danieleades
Copy link
Author

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?

@danieleades
Copy link
Author

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

@TedDriggs
Copy link
Owner

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.

@TedDriggs TedDriggs changed the title Question- how to 'map' the type of a FromDeriveInput field Allow usage of map to handle fields that don't impl FromMeta Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants