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}