mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 14:12:31 +00:00
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:
parent
88538ad183
commit
d472a88337
3 changed files with 182 additions and 37 deletions
113
lofty_attr/src/ebml.rs
Normal file
113
lofty_attr/src/ebml.rs
Normal 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()))
|
||||
}
|
|
@ -34,6 +34,7 @@
|
|||
)]
|
||||
|
||||
mod attribute;
|
||||
mod ebml;
|
||||
mod internal;
|
||||
mod lofty_file;
|
||||
mod lofty_tag;
|
||||
|
@ -66,3 +67,54 @@ pub fn tag(args_input: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let lofty_tag = LoftyTag::new(attribute, input);
|
||||
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
|
||||
});
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::macros::decode_err;
|
|||
use std::io::Read;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use lofty_attr::ebml_master_elements;
|
||||
|
||||
pub struct ElementHeader {
|
||||
pub(crate) id: VInt,
|
||||
|
@ -47,43 +48,7 @@ pub(crate) struct ChildElementDescriptor {
|
|||
pub(crate) data_type: ElementDataType,
|
||||
}
|
||||
|
||||
macro_rules! define_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_master_elements! {
|
||||
EBML: {
|
||||
id: 0x1A45_DFA3,
|
||||
children: [
|
||||
|
@ -103,6 +68,21 @@ define_master_elements! {
|
|||
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 {
|
||||
|
|
Loading…
Reference in a new issue