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

Simplify processing of structured MetaItems #25

Closed
dtolnay opened this issue Oct 2, 2016 · 6 comments
Closed

Simplify processing of structured MetaItems #25

dtolnay opened this issue Oct 2, 2016 · 6 comments

Comments

@dtolnay
Copy link
Owner

dtolnay commented Oct 2, 2016

Diesel uses the following to parse attributes of the form #[changeset_options(treat_none_as_null = "true")]:

match options_attr.value {
    syn::MetaItem::List(_, ref values) => {
        if values.len() != 1 {
            usage_err();
        }
        match values[0] {
            syn::MetaItem::NameValue(ref name, ref value)
                if name.as_ref() == "treat_none_as_null" => value == "true",
            _ => usage_err(),
        }
    }
    _ => usage_err(),
}

I expect this use case to be pretty common so let's provide helpers to make it less bad.

@dtolnay
Copy link
Owner Author

dtolnay commented Oct 2, 2016

Something like this in this case:

if let attr_pat!{ #[_(treat_none_as_null = "true")] } = options_attr {
    true
} else {
    usage_err()
}

or:

if let attr_pat!{ #[_(treat_none_as_null = ref value)] } = options_attr {
    value == "true"
} else {
    usage_err()
}

@killercup
Copy link
Contributor

Interesting idea to use a macro for this. The macro as you described it above looks for this explicit pattern, right? How'd you handle the default case, i.e., the option we are looking for is not set (but maybe others are)?

I'd've suggested introducing a special attr type for attrs that are like #[$name($($key = $val),+)] (in contrast to e.g. #[$name = $val]).

let changeset_options: OptionsAttr =
    macro_input
    .attr("changeset_options")
    .parse_as_options_attr()
    .unwrap_or_else(usage_error);
let treat_none_as_null =
    changeset_options
    .boolean_field("treat_none_as_null")
    .unwrap_or(false);

@sgrif
Copy link
Contributor

sgrif commented Oct 3, 2016

One easy win would be to drop Attribute entirely. I don't think the is_sugared_doc field does much for us.

@dtolnay
Copy link
Owner Author

dtolnay commented Jan 14, 2017

Here is a neat idea from @killercup in colin-kiegel/rust-derive-builder#32 - use Serde!

I feel like there should be an idiomatic solution to parsing syn attributes to structs. Maybe another derive crate? A small serde plugin for (de)serializing things to/from rust attributes? The possibilities are infinite! :D

#[derive(Deserialize)] struct Options { setters: SetterOptions, /* … */ }
#[derive(Deserialize)] struct GetterOptions { pattern: SetterPattern, public: bool, private: bool }
#[derive(Deserialize)] enum SetterPattern { Owned, Mutable, Immutable }

// …
let attr: syn::Attribute = attr;
let opt: Options = parse_attribute!(builder_options);

@dtolnay dtolnay changed the title Simplify pattern matching on structured MetaItems Simplify processing of structured MetaItems Jan 27, 2017
@Nemo157
Copy link

Nemo157 commented Feb 5, 2017

I wrote a little procedural macro crate for parsing attributes prom-attire. Still needs documentation written, but the initial diesel example (#[changeset_options(treat_none_as_null = "true")]) would be:

#[derive(PromAttire)]
#[attire(scope = "changeset_options")]
ChangesetOptions {
    treat_none_as_null: bool
}

let options = ChangesetOptions::from(macro_input.attrs.as_slice());

@dtolnay
Copy link
Owner Author

dtolnay commented Feb 19, 2017

Amazing. I'm happy to recommend prom-attire as the solution to this. Thanks @Nemo157!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants