Skip to main content

resource_fork_macros/
lib.rs

1//! Macro implementations for the resource-fork-rs crate. See [resource-fork-rs](../resource-fork-rs) for more details.
2use darling::FromDeriveInput;
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, DeriveInput};
6
7#[derive(FromDeriveInput, Default)]
8#[darling(default, attributes(resource))]
9struct Opts {
10    #[darling(default)]
11    code: String,
12    include_size: Option<bool>,
13    include_id: Option<bool>,
14    include_name: Option<bool>,
15}
16
17/// Make a struct readable from a resource fork
18///
19/// Macintosh 'PNT ' resource for example could be defined as:
20///
21/// ```skip
22/// #[derive(Debug, Copy, Clone, BinRead, Resource)]
23/// #[resource(code = "PNT ")]
24/// pub struct Point {
25///    pub x: i16,
26///    pub y: i16,
27/// }
28/// ```
29///
30/// and then read from a resource fork using:
31///
32/// ```skip
33/// let fork = ResourceFork::open("sample").unwrap();
34/// let point: Point = fork.read(12).unwrap();
35///
36/// assert_eq!(point.x, 12);
37/// assert_eq!(point.y, 12);
38/// ```
39///
40#[proc_macro_derive(Resource, attributes(resource))]
41pub fn derive(input: TokenStream) -> TokenStream {
42    let input = parse_macro_input!(input);
43    let opts = Opts::from_derive_input(&input).expect("Wrong options");
44    let DeriveInput { ident, .. } = input;
45
46    let make_args = match (
47        opts.include_size.unwrap_or_default(),
48        opts.include_id.unwrap_or_default(),
49        opts.include_name.unwrap_or_default(),
50    ) {
51        (true, false, false) => quote! { (size, ) },
52        (true, true, false) => quote! { (size, id, ) },
53        (true, true, true) => quote! { (size, id, name) },
54        (true, false, true) => quote! { (size, name) },
55        (false, true, false) => quote! { ( id,) },
56        (false, true, true) => quote! { ( id, name) },
57        (false, false, true) => quote! { ( name,) },
58        (false, false, false) => quote! {()},
59    };
60    let code = opts.code;
61
62    let output = quote! {
63        impl<'a> ::resource_fork::ResourceReading for #ident {
64            const FOURCC: fourcc::FourCC = fourcc::fourcc!(#code);
65
66            fn from_slice(buffer: &[u8], id: u16, name: &str) -> std::result::Result<Self, ::resource_fork::Error>
67            where
68                Self: Sized,
69            {
70                use binrw::BinReaderExt;
71                let size: usize = buffer.len();
72                let mut reader = std::io::Cursor::new(buffer);
73                reader.read_be_args(#make_args).map_err(|e| e.into())
74            }
75        }
76    };
77    output.into()
78}