TedDriggs / darling

A Rust proc-macro attribute parser
MIT License
1.02k stars 66 forks source link

Syntax for expressing enums with mixed variants #249

Closed LonerDan closed 8 months ago

LonerDan commented 1 year ago

Let's say I have the following:

#[derive(Debug, FromField)]
#[darling(attributes(my_attribute))]
struct StructFieldReceiver {
    ident: Option<syn::Ident>,
    vis: syn::Visibility,
    ty: syn::Type,
    name: String,
    sql_type: SqlType,
}

#[derive(Debug, FromMeta)]
#[darling(rename_all = "lowercase")]
enum SqlType {
// omitted variants
    BigInt, Int, SmallInt, TinyInt, Bit,
    Float(Option<u8>),
    Decimal{
        p: Option<u8>,
        s: Option<u8>
    },
// omitted variants
}

I can use the variants without type with #[my_attribute(name = "name", sql_type = "int")], but I don't know the correct syntax for specifying the Float or Decimal variants. Could you please point me in the right direction?

TedDriggs commented 1 year ago

Try #[my_attribute(name = "name", sql_type(float = 3.5)]

LonerDan commented 1 year ago

Thank you, that works. Is there a way to also allow using it without the value, so that it defaults to None? Neither sql_type(float), sql_type = float nor sql_type = "float" seem to accomplish that.

TedDriggs commented 1 year ago

I think for that you'll need to do a custom implementation of FromMeta, or use the #[darling(with = "...")] attribute on the field in question to override the parsing function.

LonerDan commented 1 year ago

Could this be done in addition to the derived FromMeta implementation (i.e. only add implementation for those cases)?

TedDriggs commented 1 year ago

Sort of - you can create a private mirror of the enum that derives FromMeta and has a conversion into the public enum, and then have your custom impl of FromMeta on the public enum delegate to the derived impl for all cases except the ones that need special handling.

I’d be willing to look at a PR or issue for adding with = “…” support to variants, which would be another way to do this.

LonerDan commented 1 year ago

Would it be possible or desired to add handling of this to the code generated for #[derive(FromMeta)]? Darling already handles correctly Option<..> fields (i.e. absence of attribute field means None), so it could behave similarly in enums.

TedDriggs commented 1 year ago

I don't think the way that darling handles absent fields would work for enum variants; that uses the FromMeta::from_none method on the field-type level, and we don't have a good way of expressing that for variant type.

I think the place that would need work would be here, where we'd want to check if all the fields of the variant were optional and then do something special. However, I'm not sure if that would compose well with newtype variants, and I think any solution we pursue here would need to do that.

LonerDan commented 1 year ago

I looked at the way the variant parsing is done from string values, and we could alter the matching a bit for the variants which are newtype or struct by using the default value or trying the from_none fallback (in a very similar way it is done in the from_list implementation). Here is a proof of concept: LonerDan/darling@3e06e62e2c72e4b8b841f90f9e92b682cb4bee17

TedDriggs commented 9 months ago

I'd suggest trying this again, as a bunch of changes in enum variant handling just merged.