lofty_attr: Add proc macro to define EBML master elements

Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
This commit is contained in:
Serial 2023-11-23 14:51:59 -05:00
parent 88538ad183
commit d472a88337
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
3 changed files with 182 additions and 37 deletions

113
lofty_attr/src/ebml.rs Normal file
View file

@ -0,0 +1,113 @@
use proc_macro::TokenStream;
use std::collections::HashSet;
use syn::parse::{Parse, Parser};
use syn::punctuated::Punctuated;
use syn::{braced, bracketed, Ident, Token};
pub(crate) struct EbmlMasterElement {
pub(crate) readable_ident: Ident,
pub(crate) info: EbmlMasterInfo,
}
impl Parse for EbmlMasterElement {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let readable_ident = input.parse::<Ident>()?;
let _: syn::Token![:] = input.parse()?;
let info;
braced!(info in input);
Ok(Self {
readable_ident,
info: info.parse::<EbmlMasterInfo>()?,
})
}
}
pub(crate) struct EbmlMasterInfo {
pub(crate) id: u64,
pub(crate) children: Vec<EbmlChildElement>,
}
impl Parse for EbmlMasterInfo {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let _id_field = input.parse::<Ident>()?;
let _: syn::Token![:] = input.parse()?;
let id = input.parse::<syn::LitInt>()?.base10_parse()?;
let _: syn::Token![,] = input.parse()?;
let _children_field = input.parse::<Ident>()?;
let _: syn::Token![:] = input.parse()?;
let children;
bracketed!(children in input);
let children = children
.parse_terminated(EbmlChildElement::parse, syn::Token![,])?
.into_iter()
.collect();
let _trailing_comma = input.parse::<Token![,]>().ok();
Ok(Self { id, children })
}
}
pub(crate) struct EbmlChildElement {
pub(crate) readable_ident: Ident,
pub(crate) info: EbmlChildInfo,
}
impl Parse for EbmlChildElement {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let readable_ident = input.parse::<Ident>()?;
let _: syn::Token![:] = input.parse()?;
let info;
braced!(info in input);
Ok(Self {
readable_ident,
info: info.parse::<EbmlChildInfo>()?,
})
}
}
pub(crate) struct EbmlChildInfo {
pub(crate) id: u64,
pub(crate) data_type: Ident,
}
impl Parse for EbmlChildInfo {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let id = input.parse::<syn::LitInt>()?.base10_parse()?;
let _: syn::Token![,] = input.parse()?;
let data_type = input.parse::<Ident>()?;
Ok(Self { id, data_type })
}
}
fn insert_element_identifiers(identifiers: &mut HashSet<Ident>, element: &EbmlMasterElement) {
identifiers.insert(element.readable_ident.clone());
for child in &element.info.children {
identifiers.insert(child.readable_ident.clone());
}
}
pub(crate) fn parse_ebml_master_elements(
input: TokenStream,
) -> syn::Result<(HashSet<Ident>, Vec<EbmlMasterElement>)> {
let mut element_identifiers = HashSet::new();
let parser = Punctuated::<EbmlMasterElement, Token![,]>::parse_terminated;
let elements = parser.parse(input)?;
for element in &elements {
insert_element_identifiers(&mut element_identifiers, element);
}
Ok((element_identifiers, elements.into_iter().collect()))
}

View file

@ -34,6 +34,7 @@
)] )]
mod attribute; mod attribute;
mod ebml;
mod internal; mod internal;
mod lofty_file; mod lofty_file;
mod lofty_tag; mod lofty_tag;
@ -66,3 +67,54 @@ pub fn tag(args_input: TokenStream, input: TokenStream) -> TokenStream {
let lofty_tag = LoftyTag::new(attribute, input); let lofty_tag = LoftyTag::new(attribute, input);
lofty_tag.emit() lofty_tag.emit()
} }
#[proc_macro]
#[doc(hidden)]
pub fn ebml_master_elements(input: TokenStream) -> TokenStream {
let ret = ebml::parse_ebml_master_elements(input);
if let Err(err) = ret {
return TokenStream::from(err.to_compile_error());
}
let (identifiers, elements) = ret.unwrap();
let identifiers_iter = identifiers.iter();
let elements_map_inserts = elements.iter().map(|element| {
let readable_ident = &element.readable_ident;
let id = element.info.id;
let children = element.info.children.iter().map(|child| {
let readable_ident = &child.readable_ident;
let id = child.info.id;
let data_type = &child.info.data_type;
quote! {
(VInt(#id), ChildElementDescriptor {
ident: ElementIdent::#readable_ident,
data_type: ElementDataType::#data_type,
})
}
});
quote! {
m.insert(
VInt(#id),
MasterElement {
id: ElementIdent::#readable_ident,
children: &[#( #children ),*][..]
}
);
}
});
TokenStream::from(quote! {
#[derive(Copy, Clone, Eq, PartialEq)]
pub(crate) enum ElementIdent {
#( #identifiers_iter ),*
}
static MASTER_ELEMENTS: once_cell::sync::Lazy<std::collections::HashMap<VInt, MasterElement>> = once_cell::sync::Lazy::new(|| {
let mut m = std::collections::HashMap::new();
#( #elements_map_inserts )*
m
});
})
}

View file

@ -5,6 +5,7 @@ use crate::macros::decode_err;
use std::io::Read; use std::io::Read;
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use lofty_attr::ebml_master_elements;
pub struct ElementHeader { pub struct ElementHeader {
pub(crate) id: VInt, pub(crate) id: VInt,
@ -47,43 +48,7 @@ pub(crate) struct ChildElementDescriptor {
pub(crate) data_type: ElementDataType, pub(crate) data_type: ElementDataType,
} }
macro_rules! define_master_elements { ebml_master_elements! {
($(
$_readable_ident:ident : {
id: $vint_id:literal,
children: [
$($_readable_child_ident:ident : { $child_id:literal, $data_ty:ident }),* $(,)?
] $(,)?
}
),+ $(,)?) => {
#[derive(Copy, Clone, Eq, PartialEq)]
pub(crate) enum ElementIdent {
$(
$_readable_ident,
$($_readable_child_ident,)*
)+
}
static MASTER_ELEMENTS: once_cell::sync::Lazy<std::collections::HashMap<VInt, MasterElement>> = once_cell::sync::Lazy::new(|| {
let mut m = std::collections::HashMap::new();
$(
m.insert(
VInt($vint_id),
MasterElement {
id: ElementIdent::$_readable_ident,
children: &[$((VInt($child_id), ChildElementDescriptor {
ident: ElementIdent::$_readable_child_ident,
data_type: ElementDataType::$data_ty,
})),*][..]
}
);
)+
m
});
}
}
define_master_elements! {
EBML: { EBML: {
id: 0x1A45_DFA3, id: 0x1A45_DFA3,
children: [ children: [
@ -103,6 +68,21 @@ define_master_elements! {
DocTypeExtensionVersion: { 0x4284, UnsignedInt }, DocTypeExtensionVersion: { 0x4284, UnsignedInt },
], ],
}, },
// The Root Element that contains all other Top-Level Elements
Segment: {
id: 0x1853_8067,
children: [
SeekHead: { 0x114D_9B74, Master },
Info: { 0x1549_A966, Master },
Cluster: { 0x1F43_B675, Master },
Tracks: { 0x1654_AE6B, Master },
Cues: { 0x1C53_BB6B, Master },
Tags: { 0x1254_C367, Master },
Attachments: { 0x1941_A469, Master },
Chapters: { 0x1043_A770, Master },
],
},
} }
struct ElementReaderContext { struct ElementReaderContext {