Skip to content

Commit 1aeb77a

Browse files
committed
lofty_attr: Add proc macro to define EBML master elements
Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
1 parent 2393063 commit 1aeb77a

File tree

3 files changed

+182
-37
lines changed

3 files changed

+182
-37
lines changed

lofty_attr/src/ebml.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use proc_macro::TokenStream;
2+
use std::collections::HashSet;
3+
use syn::parse::{Parse, Parser};
4+
use syn::punctuated::Punctuated;
5+
use syn::{braced, bracketed, Ident, Token};
6+
7+
pub(crate) struct EbmlMasterElement {
8+
pub(crate) readable_ident: Ident,
9+
pub(crate) info: EbmlMasterInfo,
10+
}
11+
12+
impl Parse for EbmlMasterElement {
13+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
14+
let readable_ident = input.parse::<Ident>()?;
15+
let _: syn::Token![:] = input.parse()?;
16+
17+
let info;
18+
braced!(info in input);
19+
20+
Ok(Self {
21+
readable_ident,
22+
info: info.parse::<EbmlMasterInfo>()?,
23+
})
24+
}
25+
}
26+
27+
pub(crate) struct EbmlMasterInfo {
28+
pub(crate) id: u64,
29+
pub(crate) children: Vec<EbmlChildElement>,
30+
}
31+
32+
impl Parse for EbmlMasterInfo {
33+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
34+
let _id_field = input.parse::<Ident>()?;
35+
let _: syn::Token![:] = input.parse()?;
36+
37+
let id = input.parse::<syn::LitInt>()?.base10_parse()?;
38+
let _: syn::Token![,] = input.parse()?;
39+
40+
let _children_field = input.parse::<Ident>()?;
41+
let _: syn::Token![:] = input.parse()?;
42+
43+
let children;
44+
bracketed!(children in input);
45+
46+
let children = children
47+
.parse_terminated(EbmlChildElement::parse, syn::Token![,])?
48+
.into_iter()
49+
.collect();
50+
51+
let _trailing_comma = input.parse::<Token![,]>().ok();
52+
53+
Ok(Self { id, children })
54+
}
55+
}
56+
57+
pub(crate) struct EbmlChildElement {
58+
pub(crate) readable_ident: Ident,
59+
pub(crate) info: EbmlChildInfo,
60+
}
61+
62+
impl Parse for EbmlChildElement {
63+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
64+
let readable_ident = input.parse::<Ident>()?;
65+
let _: syn::Token![:] = input.parse()?;
66+
67+
let info;
68+
braced!(info in input);
69+
70+
Ok(Self {
71+
readable_ident,
72+
info: info.parse::<EbmlChildInfo>()?,
73+
})
74+
}
75+
}
76+
77+
pub(crate) struct EbmlChildInfo {
78+
pub(crate) id: u64,
79+
pub(crate) data_type: Ident,
80+
}
81+
82+
impl Parse for EbmlChildInfo {
83+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
84+
let id = input.parse::<syn::LitInt>()?.base10_parse()?;
85+
let _: syn::Token![,] = input.parse()?;
86+
87+
let data_type = input.parse::<Ident>()?;
88+
89+
Ok(Self { id, data_type })
90+
}
91+
}
92+
93+
fn insert_element_identifiers(identifiers: &mut HashSet<Ident>, element: &EbmlMasterElement) {
94+
identifiers.insert(element.readable_ident.clone());
95+
for child in &element.info.children {
96+
identifiers.insert(child.readable_ident.clone());
97+
}
98+
}
99+
100+
pub(crate) fn parse_ebml_master_elements(
101+
input: TokenStream,
102+
) -> syn::Result<(HashSet<Ident>, Vec<EbmlMasterElement>)> {
103+
let mut element_identifiers = HashSet::new();
104+
105+
let parser = Punctuated::<EbmlMasterElement, Token![,]>::parse_terminated;
106+
let elements = parser.parse(input)?;
107+
108+
for element in &elements {
109+
insert_element_identifiers(&mut element_identifiers, element);
110+
}
111+
112+
Ok((element_identifiers, elements.into_iter().collect()))
113+
}

lofty_attr/src/lib.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
)]
3535

3636
mod attribute;
37+
mod ebml;
3738
mod internal;
3839
mod lofty_file;
3940
mod lofty_tag;
@@ -66,3 +67,54 @@ pub fn tag(args_input: TokenStream, input: TokenStream) -> TokenStream {
6667
let lofty_tag = LoftyTag::new(attribute, input);
6768
lofty_tag.emit()
6869
}
70+
71+
#[proc_macro]
72+
#[doc(hidden)]
73+
pub fn ebml_master_elements(input: TokenStream) -> TokenStream {
74+
let ret = ebml::parse_ebml_master_elements(input);
75+
76+
if let Err(err) = ret {
77+
return TokenStream::from(err.to_compile_error());
78+
}
79+
80+
let (identifiers, elements) = ret.unwrap();
81+
let identifiers_iter = identifiers.iter();
82+
let elements_map_inserts = elements.iter().map(|element| {
83+
let readable_ident = &element.readable_ident;
84+
let id = element.info.id;
85+
let children = element.info.children.iter().map(|child| {
86+
let readable_ident = &child.readable_ident;
87+
let id = child.info.id;
88+
let data_type = &child.info.data_type;
89+
quote! {
90+
(VInt(#id), ChildElementDescriptor {
91+
ident: ElementIdent::#readable_ident,
92+
data_type: ElementDataType::#data_type,
93+
})
94+
}
95+
});
96+
97+
quote! {
98+
m.insert(
99+
VInt(#id),
100+
MasterElement {
101+
id: ElementIdent::#readable_ident,
102+
children: &[#( #children ),*][..]
103+
}
104+
);
105+
}
106+
});
107+
108+
TokenStream::from(quote! {
109+
#[derive(Copy, Clone, Eq, PartialEq)]
110+
pub(crate) enum ElementIdent {
111+
#( #identifiers_iter ),*
112+
}
113+
114+
static MASTER_ELEMENTS: once_cell::sync::Lazy<std::collections::HashMap<VInt, MasterElement>> = once_cell::sync::Lazy::new(|| {
115+
let mut m = std::collections::HashMap::new();
116+
#( #elements_map_inserts )*
117+
m
118+
});
119+
})
120+
}

src/ebml/element_reader.rs

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::macros::decode_err;
55
use std::io::Read;
66

77
use byteorder::{BigEndian, ReadBytesExt};
8+
use lofty_attr::ebml_master_elements;
89

910
pub struct ElementHeader {
1011
pub(crate) id: VInt,
@@ -47,43 +48,7 @@ pub(crate) struct ChildElementDescriptor {
4748
pub(crate) data_type: ElementDataType,
4849
}
4950

50-
macro_rules! define_master_elements {
51-
($(
52-
$_readable_ident:ident : {
53-
id: $vint_id:literal,
54-
children: [
55-
$($_readable_child_ident:ident : { $child_id:literal, $data_ty:ident }),* $(,)?
56-
] $(,)?
57-
}
58-
),+ $(,)?) => {
59-
#[derive(Copy, Clone, Eq, PartialEq)]
60-
pub(crate) enum ElementIdent {
61-
$(
62-
$_readable_ident,
63-
$($_readable_child_ident,)*
64-
)+
65-
}
66-
67-
static MASTER_ELEMENTS: once_cell::sync::Lazy<std::collections::HashMap<VInt, MasterElement>> = once_cell::sync::Lazy::new(|| {
68-
let mut m = std::collections::HashMap::new();
69-
$(
70-
m.insert(
71-
VInt($vint_id),
72-
MasterElement {
73-
id: ElementIdent::$_readable_ident,
74-
children: &[$((VInt($child_id), ChildElementDescriptor {
75-
ident: ElementIdent::$_readable_child_ident,
76-
data_type: ElementDataType::$data_ty,
77-
})),*][..]
78-
}
79-
);
80-
)+
81-
m
82-
});
83-
}
84-
}
85-
86-
define_master_elements! {
51+
ebml_master_elements! {
8752
EBML: {
8853
id: 0x1A45_DFA3,
8954
children: [
@@ -103,6 +68,21 @@ define_master_elements! {
10368
DocTypeExtensionVersion: { 0x4284, UnsignedInt },
10469
],
10570
},
71+
72+
// The Root Element that contains all other Top-Level Elements
73+
Segment: {
74+
id: 0x1853_8067,
75+
children: [
76+
SeekHead: { 0x114D_9B74, Master },
77+
Info: { 0x1549_A966, Master },
78+
Cluster: { 0x1F43_B675, Master },
79+
Tracks: { 0x1654_AE6B, Master },
80+
Cues: { 0x1C53_BB6B, Master },
81+
Tags: { 0x1254_C367, Master },
82+
Attachments: { 0x1941_A469, Master },
83+
Chapters: { 0x1043_A770, Master },
84+
],
85+
},
10686
}
10787

10888
struct ElementReaderContext {

0 commit comments

Comments
 (0)