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
extern crate proc_macro;

mod ast;
mod attr;
mod easy_ebml;

use proc_macro::TokenStream;
use syn::{ItemEnum, Error};
use crate::easy_ebml::EasyEBML;

///
/// Attribute that derives implementations of [`EbmlSpecification`][spec] and [`EbmlTag`][tag] for an enum.
///
/// This macro is intended to make implementing the traits in ebml-iterable-specification easier to manage.  Rather than requiring handwritten implementations for [`EbmlSpecification`][spec] and [`EbmlTag`][tag] methods, this macro understands attributes assigned to enum members and generates an implementation accordingly.
///
/// When deriving `EbmlSpecification` for an enum, the following attributes are required for each variant:
///   * __#[id(`u64`)]__ - This attribute specifies the "id" of the tag. e.g. `0x1a45dfa3`
///   * __#[data_type(`TagDataType`)]__ - This attribute specifies the type of data contained in the tag. e.g. `TagDataType::UnsignedInt`
///
/// The following attribute is optional for each variant:
///   * __#[parent(OtherVariantName)]__ - This attribute specifies the direct ancestor of the current element.  If this attribute is not present, the variant is treated as a Root element.
/// 
/// # Note
///
/// This attribute modifies the variants in the enumeration by adding fields to them.  It also will add a `RawTag(u64, Vec<u8>)` variant to the enumeration.
///
/// [spec]: ebml_iterable_specification::EbmlSpecification
/// [tag]: ebml_iterable_specification::EbmlTag

#[proc_macro_attribute]
pub fn ebml_specification(_args: TokenStream, input: TokenStream) -> TokenStream {
    let mut input = match syn::parse::<ItemEnum>(input) {
        Ok(syntax_tree) => syntax_tree,
        Err(err) => {
            return TokenStream::from(Error::new(err.span(), "#[ebml_specification] attribute can only be applied to enums").to_compile_error())
        },
    };

    attr::impl_ebml_specification(&mut input)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

///
/// Macro that makes writing an EBML spec easy.
/// 
/// This provides an even easier alternative to create implementations of the [`EbmlSpecification`][spec] and [`EbmlTag`][tag] traits than using the [`[#ebml_specification]`][macro] attribute.  As a bonus, your spec will be more legible and maintainable!
/// 
/// As an example, compare the following equivalent definitions:
/// ```
/// # use ebml_iterable_specification_derive::ebml_specification;
/// # use ebml_iterable_specification::TagDataType::{Master, UnsignedInt};
/// # pub mod ebml_iterable { pub mod specs { 
/// #    pub use ebml_iterable_specification_derive::ebml_specification as ebml_specification; 
/// #    pub use ebml_iterable_specification::EbmlSpecification as EbmlSpecification;
/// #    pub use ebml_iterable_specification::EbmlTag as EbmlTag;
/// #    pub use ebml_iterable_specification::TagDataType as TagDataType;
/// #    pub use ebml_iterable_specification::Master as Master;
/// # }}
/// #[ebml_specification]
/// #[derive(Clone)]
/// enum Example {
///   #[id(0x01)]
///   #[data_type(Master)]
///   Root,
///
///   #[id(0x02)]
///   #[data_type(Master)]
///   #[parent(Root)]
///   Parent,
///
///   #[id(0x100)]
///   #[data_type(UnsignedInt)]
///   #[parent(Parent)]
///   Data,
/// }
/// ```
/// vs
/// ```
/// # use ebml_iterable_specification_derive::easy_ebml;
/// # use ebml_iterable_specification::TagDataType;
/// # use ebml_iterable_specification::TagDataType::{Master, UnsignedInt};
/// # pub mod ebml_iterable { pub mod specs { 
/// #    pub use ebml_iterable_specification_derive::ebml_specification as ebml_specification; 
/// #    pub use ebml_iterable_specification::EbmlSpecification as EbmlSpecification;
/// #    pub use ebml_iterable_specification::EbmlTag as EbmlTag;
/// #    pub use ebml_iterable_specification::TagDataType as TagDataType;
/// #    pub use ebml_iterable_specification::Master as Master;
/// # }}
/// easy_ebml! {
///   #[derive(Clone)]
///   enum Example {
///     Root                : Master = 0x01,
///     Root/Parent         : Master = 0x02,
///     Root/Parent/Data    : UnsignedInt = 0x100,
///   }
/// }
/// ```
/// 
/// Behind the scenes `easy_ebml!` still uses the existing [`[#ebml_specification]`][macro] attribute macro, so the final output of this macro will remain identical.
/// 
/// [spec]: ebml_iterable_specification::EbmlSpecification
/// [tag]: ebml_iterable_specification::EbmlTag
/// [macro]: macro@crate::ebml_specification

#[proc_macro]
pub fn easy_ebml(input: TokenStream) -> TokenStream {
    let input = match syn::parse::<EasyEBML>(input) {
        Ok(syntax_tree) => syntax_tree,
        Err(err) => {
            return TokenStream::from(Error::new(err.span(), "easy_ebml! {} content must be of format: enum Name {\
                Root: Type = id,\
                Path/Of/Component: Type = id,\
                // example\
                Crc32: Binary = 0xbf,\
                Ebml: Master = 0x1a45dfa3,\
                Ebml/EbmlVersion: UnsignedInt = 0x4286,\
            }").to_compile_error())
        },
    };

    input.implement().unwrap_or_else(|err| err.to_compile_error()).into()
}