Open danieleades opened 10 months ago
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:
default
at the field level does the trick. Note that I did run into #257 while testing this.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).
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.
i may be misunderstanding the use of the 'mapping' feature
I'm attempting to parse a PathBuf from a derive input. something like-
to parse-
is this how 'map' is supposed to be used?
Same question for parsing a comma-separated list into a
Vec<String>