1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

//!
//! This crate provides helper procedural macros for type conversions,
//! it supports struct(s) with named and unnamed fields, enums.
//!
//! # Example
//!
//! ```
//! #[derive(Debug, Clone, Eq, PartialEq, derive_more::From, derive_more::Into)]
//! struct I64(pub i64);
//!
//! #[derive(Debug, Clone, Eq, PartialEq, mina_serialization_types_macros::AutoFrom)]
//! #[auto_from(Bar)]
//! enum Foo {
//!     V1,
//!     V2(i64),
//!     V3(i64, Option<i64>, Vec<i64>, Box<i64>),
//!     V4 {
//!         f1: i64,
//!     },
//!     V5 {
//!         f1: i64,
//!         f2: Option<i64>,
//!         f3: Vec<i64>,
//!         f4: Box<i64>,
//!     },
//! }

//! #[derive(Debug, Clone, Eq, PartialEq)]
//! enum Bar {
//!     V1,
//!     V2(I64),
//!     V3(I64, Option<I64>, Vec<I64>, Box<I64>),
//!     V4 {
//!         f1: I64,
//!     },
//!     V5 {
//!         f1: I64,
//!         f2: Option<I64>,
//!         f3: Vec<I64>,
//!         f4: Box<I64>,
//!     },
//! }

//! type BarV1 = versioned::Versioned<Bar, 1>;
//! let foo = Foo::V5 {
//!     f1: 8,
//!     f2: Some(9),
//!     f3: vec![10, 11, 12],
//!     f4: Box::new(13),
//! };
//!
//! let bar: Bar = foo.clone().into();
//! let bar_v1: BarV1 = foo.clone().into();
//! let foo_from_bar: Foo = bar.into();
//! let foo_from_bar_v1: Foo = bar_v1.into();
//! assert_eq!(foo, foo_from_bar);
//! assert_eq!(foo, foo_from_bar_v1);
//! ```
//!

#![deny(warnings)]
#![deny(missing_docs)]

mod auto_from;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DataEnum, DeriveInput, FieldsNamed, FieldsUnnamed};

/// A derive macro that automatically implements [From] trait between the annotated type
/// and types including the attributed target type(s) and their versioned types,
/// when the target type has identical field names with the annotated one, and each pair of the fields
/// are convertible between each other
#[proc_macro_derive(AutoFrom, attributes(auto_from))]
pub fn auto_from_macro(input: TokenStream) -> TokenStream {
    let DeriveInput {
        attrs, ident, data, ..
    } = parse_macro_input!(input);

    let target_types: Vec<proc_macro2::TokenStream> =
        auto_from::parse_types_from_attr(attrs.as_slice());

    if target_types.is_empty() {
        return Default::default();
    }

    match data {
        syn::Data::Struct(s) => match s.fields {
            syn::Fields::Named(FieldsNamed { named, .. }) => {
                if let Some(ts) = auto_from::auto_from_for_struct_with_named_fields(
                    &ident,
                    target_types.as_slice(),
                    named,
                ) {
                    return ts;
                }
            }
            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
                if let Some(ts) = auto_from::auto_from_for_struct_with_unnamed_fields(
                    &ident,
                    target_types.as_slice(),
                    unnamed,
                ) {
                    return ts;
                }
            }
            _ => unimplemented!(),
        },
        syn::Data::Enum(DataEnum { variants, .. }) => {
            if let Some(ts) =
                auto_from::auto_from_for_enum(&ident, target_types.as_slice(), variants)
            {
                return ts;
            }
        }
        _ => unimplemented!(),
    };

    Default::default()
}