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 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
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue