aboutsummaryrefslogtreecommitdiff
path: root/ebml_derive/src/lib.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-01-13 20:21:45 +0100
committermetamuffin <metamuffin@disroot.org>2023-01-13 20:21:45 +0100
commit36e91c20eb59e76d5aeb35e644e7fb391f346dc6 (patch)
tree45ea6dd876ec3af8e38857bce6d26faf630011de /ebml_derive/src/lib.rs
parent8fc4b9792044d82e729e8b4ef993c6391d711c5b (diff)
downloadjellything-36e91c20eb59e76d5aeb35e644e7fb391f346dc6.tar
jellything-36e91c20eb59e76d5aeb35e644e7fb391f346dc6.tar.bz2
jellything-36e91c20eb59e76d5aeb35e644e7fb391f346dc6.tar.zst
proc macro works
Diffstat (limited to 'ebml_derive/src/lib.rs')
-rw-r--r--ebml_derive/src/lib.rs177
1 files changed, 132 insertions, 45 deletions
diff --git a/ebml_derive/src/lib.rs b/ebml_derive/src/lib.rs
index 2c9dd9c..07e60f9 100644
--- a/ebml_derive/src/lib.rs
+++ b/ebml_derive/src/lib.rs
@@ -1,63 +1,150 @@
-use proc_macro::{Delimiter, TokenStream, TokenTree};
+use proc_macro::{token_stream, Delimiter, Span, TokenStream, TokenTree};
use quote::quote;
+use syn::{Fields, FieldsUnnamed, Ident, Variant};
struct Tag {
id: u64,
- path: Vec<String>,
- name: String,
+ global: bool,
+ path: Vec<u64>,
+ name: Ident,
r#type: Option<String>, // None -> Master
}
#[proc_macro]
pub fn define_ebml(ts: TokenStream) -> TokenStream {
- let mut next_glob = false;
let mut ts = ts.into_iter();
-
let mut tags = vec![];
+ parse_kt(&mut tags, &mut ts, vec![]);
- while let Some(t) = ts.next() {
- match t {
- TokenTree::Ident(ident) => {
- let ident = ident.to_string();
- if &ident == "global" {
- next_glob = true;
- } else {
- let glob = next_glob;
- next_glob = false;
- let id = if let Some(TokenTree::Group(gr)) = ts.next() {
- assert_eq!(gr.delimiter(), Delimiter::Bracket);
- let mut ts = gr.stream().into_iter();
- if let TokenTree::Literal(lit) = ts.next().unwrap() {
- u64::from_str_radix(&lit.to_string()[2..], 16).unwrap()
- } else {
- panic!("literal expected")
- }
- } else {
- panic!("group expected")
- };
- if let Some(TokenTree::Punct(p)) = ts.next() {
- assert_eq!(p.as_char(), ':')
- } else {
- panic!("colon expected")
- }
- match ts.next() {
- Some(TokenTree::Group(_)) => {}
- Some(TokenTree::Ident(ident)) => {
- let r#type = ident.to_string();
- eprintln!("global={glob} id={id}, type={}", r#type);
+ let enum_variants = tags
+ .iter()
+ .map(|e| Variant {
+ ident: e.name.clone(),
+ attrs: vec![],
+ fields: Fields::Unnamed(
+ syn::parse2::<FieldsUnnamed>(match e.r#type.clone() {
+ None => quote!((Master)),
+ Some(r#type) => match r#type.as_str() {
+ "Uint" => quote!((u64)),
+ "Utf8" => quote!((String)),
+ "Binary" => quote!((Vec<u8>)),
+ _ => panic!("unsupported type {}", r#type),
+ },
+ })
+ .expect("parse type"),
+ ),
+ discriminant: None,
+ })
+ .collect::<Vec<_>>();
+
+ let path_match = tags
+ .iter()
+ .map(|e| {
+ let name = &e.name;
+ let mut path = e.path.clone();
+ path.reverse();
+ if e.global {
+ quote! { Self::#name(_) => None }
+ } else {
+ quote! { Self::#name(_) => Some(&[#(#path),*]) }
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let parse_match = tags
+ .iter()
+ .map(|Tag { id, name, .. }| {
+ quote! { #id => Self::#name(crate::ValueFromBuf::from_buf(data)?) }
+ })
+ .collect::<Vec<_>>();
+
+ quote! {
+ use crate::Master;
+ pub enum MatroskaTag {
+ #(#enum_variants),*
+ }
+ impl MatroskaTag {
+ /// returns path in **reverse** order or None if global.
+ pub fn path(&self) -> Option<&'static [u64]> {
+ match self { #(#path_match),* }
+ }
+ pub fn parse(id: u64, data: &[u8]) -> anyhow::Result<Self> {
+ Ok(match id { #(#parse_match),*, _ => anyhow::bail!("unknown id") })
+ }
+ }
+ }
+ .into()
+}
+
+fn parse_kt(tags: &mut Vec<Tag>, ts: &mut token_stream::IntoIter, path: Vec<u64>) {
+ let mut next_glob = false;
+ loop {
+ let global = next_glob;
+ next_glob = false;
- }
- _ => panic!("group or ident expected"),
- }
- if let Some(TokenTree::Punct(p)) = ts.next() {
- assert_eq!(p.as_char(), ',')
- } else {
- panic!("colon expected")
- }
+ let name = if let Some(tt) = ts.next() {
+ if let TokenTree::Ident(name) = tt {
+ if &name.to_string() == "global" {
+ next_glob = true;
+ continue;
}
+ name.to_string()
+ } else {
+ panic!("expected ident")
}
- x => panic!("unexpected {x:?}"),
+ } else {
+ break;
+ };
+
+ let id = if let Some(TokenTree::Group(gr)) = ts.next() {
+ assert_eq!(gr.delimiter(), Delimiter::Bracket);
+ let mut ts = gr.stream().into_iter();
+ if let TokenTree::Literal(lit) = ts.next().unwrap() {
+ u64::from_str_radix(&lit.to_string()[2..], 16).unwrap()
+ } else {
+ panic!("literal expected")
+ }
+ } else {
+ panic!("group expected")
+ };
+ if let Some(TokenTree::Punct(p)) = ts.next() {
+ assert_eq!(p.as_char(), ':')
+ } else {
+ panic!("colon expected")
+ }
+ match ts.next() {
+ Some(TokenTree::Group(gr)) => {
+ // eprintln!("entering group");
+ let mut ts = gr.stream().into_iter();
+ tags.push(Tag {
+ global,
+ id,
+ name: Ident::new(&name, Span::call_site().into()),
+ path: path.clone(),
+ r#type: None,
+ });
+ let mut path = path.clone();
+ path.push(id);
+ parse_kt(tags, &mut ts, path);
+ // eprintln!("leaving group");
+ }
+ Some(TokenTree::Ident(r#type)) => {
+ let r#type = r#type.to_string();
+ // eprintln!("global={global} id={id}, type={}", r#type);
+ tags.push(Tag {
+ id,
+ name: Ident::new(&name, Span::call_site().into()),
+ path: path.clone(),
+ global,
+ r#type: Some(r#type),
+ })
+ }
+ _ => panic!("group or ident expected"),
+ }
+ if let Some(TokenTree::Punct(p)) = ts.next() {
+ assert_eq!(p.as_char(), ',')
+ } else {
+ panic!("colon expected")
}
}
- quote! {}.into()
}