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

Add FromVariant discriminant magic field #105

Merged
merged 1 commit into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## Unreleased
- Add `discriminant` magic field when deriving `FromVariant` [#105](https://github.com/TedDriggs/darling/pull/105)

## v0.10.2 (October 30, 2019)
- Bump syn dependency to 1.0.1 [#83](https://github.com/TedDriggs/darling/pull/83)

Expand Down
24 changes: 24 additions & 0 deletions core/src/codegen/from_variant_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,28 @@ use util::PathList;

pub struct FromVariantImpl<'a> {
pub base: TraitImpl<'a>,
/// If set, the ident of the field into which the variant ident should be placed.
///
/// This is one of `darling`'s "magic fields", which allow a type deriving a `darling`
/// trait to get fields from the input `syn` element added to the deriving struct
/// automatically.
pub ident: Option<&'a Ident>,
/// If set, the ident of the field into which the transformed output of the input
/// variant's fields should be placed.
///
/// This is one of `darling`'s "magic fields".
pub fields: Option<&'a Ident>,
/// If set, the ident of the field into which the forwarded attributes of the input
/// variant should be placed.
///
/// This is one of `darling`'s "magic fields".
pub attrs: Option<&'a Ident>,
/// If set, the ident of the field into which the discriminant of the input variant
/// should be placed. The receiving field must be an `Option` as not all enums have
/// discriminants.
///
/// This is one of `darling`'s "magic fields".
pub discriminant: Option<&'a Ident>,
pub attr_names: &'a PathList,
pub forward_attrs: Option<&'a ForwardAttrs>,
pub from_ident: bool,
Expand All @@ -25,6 +44,10 @@ impl<'a> ToTokens for FromVariantImpl<'a> {
.ident
.as_ref()
.map(|i| quote!(#i: #input.ident.clone(),));
let passed_discriminant = self
.discriminant
.as_ref()
.map(|i| quote!(#i: #input.discriminant.as_ref().map(|(_, expr)| expr.clone()),));
let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,));
let passed_fields = self
.fields
Expand Down Expand Up @@ -68,6 +91,7 @@ impl<'a> ToTokens for FromVariantImpl<'a> {

::darling::export::Ok(Self {
#passed_ident
#passed_discriminant
#passed_attrs
#passed_fields
#inits
Expand Down
9 changes: 9 additions & 0 deletions core/src/options/from_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use {FromMeta, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FromVariantOptions {
pub base: OuterFrom,
/// The field on the deriving struct into which the discriminant expression
/// should be placed by the derived `FromVariant` impl.
pub discriminant: Option<Ident>,
pub fields: Option<Ident>,
pub supports: Option<DataShape>,
}
Expand All @@ -17,6 +20,7 @@ impl FromVariantOptions {
pub fn new(di: &DeriveInput) -> Result<Self> {
(FromVariantOptions {
base: OuterFrom::start(di),
discriminant: Default::default(),
fields: Default::default(),
supports: Default::default(),
})
Expand All @@ -30,6 +34,7 @@ impl<'a> From<&'a FromVariantOptions> for FromVariantImpl<'a> {
FromVariantImpl {
base: (&v.base.container).into(),
ident: v.base.ident.as_ref(),
discriminant: v.discriminant.as_ref(),
fields: v.fields.as_ref(),
attrs: v.base.attrs.as_ref(),
attr_names: &v.base.attr_names,
Expand Down Expand Up @@ -60,6 +65,10 @@ impl ParseData for FromVariantOptions {
.as_ref()
.map(|v| v.as_str())
{
Some("discriminant") => {
self.discriminant = field.ident.clone();
Ok(())
}
Some("fields") => {
self.fields = field.ident.clone();
Ok(())
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
//! |`bounds`|`Vec<syn::TypeParamBound>`|The bounds applied to the type param|
//! |`default`|`Option<syn::Type>`|The default type of the parameter, if one exists|
//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in type param. These are controlled using the `forward_attrs` attribute.|
//!
//! ### `FromVariant`
//! |---|---|---|
//! |`ident`|`syn::Ident`|The identifier of the passed-in variant|
//! |`discriminant`|`Option<syn::Expr>`|For a variant such as `Example = 2`, the `2`|
//! |`fields`|`Option<darling::ast::Fields<__>>`|The fields associated with the variant|
//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in variant. These are controlled using the `forward_attrs` attribute.|

extern crate core;
extern crate darling_core;
Expand Down
36 changes: 35 additions & 1 deletion tests/from_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
extern crate darling;
extern crate syn;

use darling::FromVariant;
use syn::{spanned::Spanned, Expr, ExprLit, LitInt};

#[derive(FromVariant)]
#[darling(from_ident, attributes(hello))]
#[allow(dead_code)]
pub struct Lorem {
ident: syn::Ident,
into: Option<bool>,
skip: Option<bool>,
discriminant: Option<syn::Expr>,
fields: darling::ast::Fields<syn::Type>,
}

Expand All @@ -18,10 +22,40 @@ impl From<syn::Ident> for Lorem {
ident,
into: Default::default(),
skip: Default::default(),
discriminant: None,
fields: darling::ast::Style::Unit.into(),
}
}
}

#[test]
fn expansion() {}
fn discriminant() {
let input: syn::DeriveInput = syn::parse_str(
r#"
pub enum Test {
Works = 1,
AlsoWorks = 2,
}
"#,
)
.unwrap();

let span = input.span();
if let syn::Data::Enum(enm) = input.data {
let lorem = Lorem::from_variant(
enm.variants
.first()
.expect("Hardcoded input has one variant"),
)
.expect("FromVariant can process the discriminant");
assert_eq!(
lorem.discriminant,
Some(Expr::Lit(ExprLit {
attrs: vec![],
lit: LitInt::new("1", span).into(),
}))
)
} else {
panic!("Data should be enum");
}
}