truehdd_macros/
lib.rs

1use darling::Error;
2use darling::ast::NestedMeta;
3use quote::quote;
4use syn::{Data, DeriveInput, Fields, ItemStruct, parse_macro_input};
5
6use proc_macro::TokenStream;
7
8#[proc_macro_derive(ToBytes)]
9pub fn derive_to_bytes(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let name = input.ident;
12
13    let fields: Vec<syn::Member> = match input.data {
14        Data::Struct(ref s) => match s.fields {
15            Fields::Named(ref nf) => nf
16                .named
17                .iter()
18                .map(|f| f.ident.clone().unwrap().into())
19                .collect(),
20            Fields::Unnamed(ref uf) => uf
21                .unnamed
22                .iter()
23                .enumerate()
24                .map(|(i, _)| syn::Index::from(i).into())
25                .collect(),
26            Fields::Unit => Vec::new(),
27        },
28        _ => unreachable!("ToBytes can only be derived for structs"),
29    };
30
31    let expanded = quote! {
32        impl crate::byteorder::WriteBytesBe for #name {
33            fn write_be(&self, dst: &mut Vec<u8>) {
34                #( crate::byteorder::WriteBytesBe::write_be(&self.#fields, dst); )*
35            }
36        }
37
38        impl crate::byteorder::WriteBytesLe for #name {
39            fn write_le(&self, dst: &mut Vec<u8>) {
40                #( crate::byteorder::WriteBytesLe::write_le(&self.#fields, dst); )*
41            }
42        }
43    };
44
45    TokenStream::from(expanded)
46}
47
48#[proc_macro_attribute]
49pub fn caf_chunk_type(attr: TokenStream, item: TokenStream) -> TokenStream {
50    let args = match NestedMeta::parse_meta_list(attr.into()) {
51        Ok(v) => v,
52        Err(e) => {
53            return TokenStream::from(Error::from(e).write_errors());
54        }
55    };
56
57    let type_bytes = match &args[0] {
58        NestedMeta::Lit(syn::Lit::ByteStr(bs)) => bs.value(),
59        _ => panic!("chunk_type expects a byte string, e.g. b\"desc\""),
60    };
61
62    if type_bytes.len() != 4 {
63        return TokenStream::from(
64            syn::Error::new_spanned(&args[0], "chunk_type expects 4 bytes").to_compile_error(),
65        );
66    }
67    let type_bytes_tokens = {
68        let b = type_bytes;
69        quote! {[#(#b),*]}
70    };
71
72    let input = parse_macro_input!(item as ItemStruct);
73    let name = &input.ident;
74
75    let expanded = quote! {
76        #input
77
78        impl CAFChunk for #name {
79            fn chunk_type(&self) -> &[u8; 4] {
80                const BYTES: [u8; 4] = #type_bytes_tokens;
81                &BYTES
82            }
83
84            fn chunk_data(&self) -> Vec<u8> {
85                let mut vec = Vec::new();
86                self.write_be(&mut vec);
87                vec
88            }
89        }
90    };
91    TokenStream::from(expanded)
92}