ebml_iterable_specification_derive/
lib.rs

1extern crate proc_macro;
2
3mod ast;
4mod attr;
5mod easy_ebml;
6mod pathing;
7
8use proc_macro::TokenStream;
9use syn::{ItemEnum, Error};
10use crate::easy_ebml::EasyEBML;
11
12///
13/// Attribute that derives implementations of [`EbmlSpecification`][spec] and [`EbmlTag`][tag] for an enum.
14///
15/// 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.
16///
17/// When deriving `EbmlSpecification` for an enum, the following attributes are required for each variant:
18///   * __#[id(`u64`)]__ - This attribute specifies the "id" of the tag. e.g. `0x1a45dfa3`
19///   * __#[data_type(`TagDataType`)]__ - This attribute specifies the type of data contained in the tag. e.g. `TagDataType::UnsignedInt`
20///
21/// The following attribute is optional for each variant:
22///   * __#[doc_path(Path/To/Element)]__ - This attribute specifies the document path of the current element.  If this attribute is not present, the variant is treated as a Root element.  Global elements can be defined with wildcard paths, e.g. #[doc_path(Segment/(1-)/)].
23/// 
24/// # Note
25///
26/// This attribute modifies the variants in the enumeration by adding fields to them.  It also will add the following variants to the enum:
27/// - `Crc32(Vec<u8>)` - global tag defined in the EBML spec
28/// - `Void(Vec<u8>)` - global tag defined in the EBML spec
29/// - `RawTag(u64, Vec<u8>)` - used to support reading "unknown" tags that aren't in the spec
30///
31/// [spec]: ebml_iterable_specification::EbmlSpecification
32/// [tag]: ebml_iterable_specification::EbmlTag
33
34#[proc_macro_attribute]
35pub fn ebml_specification(_args: TokenStream, input: TokenStream) -> TokenStream {
36    let mut input = match syn::parse::<ItemEnum>(input) {
37        Ok(syntax_tree) => syntax_tree,
38        Err(err) => {
39            return TokenStream::from(Error::new(err.span(), "#[ebml_specification] attribute can only be applied to enums").to_compile_error())
40        },
41    };
42
43    attr::impl_ebml_specification(&mut input)
44        .unwrap_or_else(|err| err.to_compile_error())
45        .into()
46}
47
48///
49/// Macro that makes writing an EBML spec easy.
50/// 
51/// 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!
52/// 
53/// As an example, compare the following equivalent definitions:
54/// ```
55/// # use ebml_iterable_specification_derive::ebml_specification;
56/// # use ebml_iterable_specification::TagDataType::{Master, UnsignedInt};
57/// # pub mod ebml_iterable { pub mod specs { 
58/// #    pub use ebml_iterable_specification_derive::ebml_specification as ebml_specification; 
59/// #    pub use ebml_iterable_specification::EbmlSpecification as EbmlSpecification;
60/// #    pub use ebml_iterable_specification::EbmlTag as EbmlTag;
61/// #    pub use ebml_iterable_specification::TagDataType as TagDataType;
62/// #    pub use ebml_iterable_specification::Master as Master;
63/// #    pub use ebml_iterable_specification::PathPart as PathPart;
64/// # }}
65/// #[ebml_specification]
66/// #[derive(Clone)]
67/// enum Example {
68///   #[id(0x01)]
69///   #[data_type(Master)]
70///   Root,
71///
72///   #[id(0x02)]
73///   #[data_type(Master)]
74///   #[doc_path(Root)]
75///   Parent,
76///
77///   #[id(0x100)]
78///   #[data_type(UnsignedInt)]
79///   #[doc_path(Root/Parent)]
80///   Data,
81/// }
82/// ```
83/// vs
84/// ```
85/// # use ebml_iterable_specification_derive::easy_ebml;
86/// # use ebml_iterable_specification::TagDataType;
87/// # use ebml_iterable_specification::TagDataType::{Master, UnsignedInt};
88/// # pub mod ebml_iterable { pub mod specs { 
89/// #    pub use ebml_iterable_specification_derive::ebml_specification as ebml_specification; 
90/// #    pub use ebml_iterable_specification::EbmlSpecification as EbmlSpecification;
91/// #    pub use ebml_iterable_specification::EbmlTag as EbmlTag;
92/// #    pub use ebml_iterable_specification::TagDataType as TagDataType;
93/// #    pub use ebml_iterable_specification::Master as Master;
94/// #    pub use ebml_iterable_specification::PathPart as PathPart;
95/// # }}
96/// easy_ebml! {
97///   #[derive(Clone)]
98///   enum Example {
99///     Root                : Master = 0x01,
100///     Root/Parent         : Master = 0x02,
101///     Root/Parent/Data    : UnsignedInt = 0x100,
102///   }
103/// }
104/// ```
105/// 
106/// Behind the scenes `easy_ebml!` still uses the existing [`[#ebml_specification]`][macro] attribute macro, so the final output of this macro will remain identical.
107/// 
108/// [spec]: ebml_iterable_specification::EbmlSpecification
109/// [tag]: ebml_iterable_specification::EbmlTag
110/// [macro]: macro@crate::ebml_specification
111
112#[proc_macro]
113pub fn easy_ebml(input: TokenStream) -> TokenStream {
114    let input = match syn::parse::<EasyEBML>(input) {
115        Ok(syntax_tree) => syntax_tree,
116        Err(err) => {
117            return TokenStream::from(Error::new(err.span(), "easy_ebml! {} content must be of format: enum Name {\
118                Root: Type = id,\
119                Path/Of/Component: Type = id,\
120                // example\
121                Ebml: Master = 0x1a45dfa3,\
122                Ebml/EbmlVersion: UnsignedInt = 0x4286,\
123                // global elements can be used in paths, example:\
124                (1-)/Crc32: Binary = 0xbf,\
125            }").to_compile_error())
126        },
127    };
128
129    input.implement().unwrap_or_else(|err| err.to_compile_error()).into()
130}