nipdf_macro/
lib.rs

1#![allow(clippy::expect_used)]
2#![allow(clippy::unwrap_used)]
3#![allow(clippy::panic)]
4#![allow(clippy::todo)]
5
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::quote;
9use syn::{
10    Arm, Expr, ExprLit, Fields, FieldsUnnamed, Ident, ItemEnum, ItemStruct, Lit, LitStr, Meta, Pat,
11    Token, parse_macro_input, parse_quote,
12};
13
14/// Generate `impl TryFrom` for enum that convert Object::Name to enum variant
15/// Name equals to variant name
16#[proc_macro_derive(TryFromNameObject)]
17pub fn try_from_name_object(input: TokenStream) -> TokenStream {
18    let enum_t = parse_macro_input!(input as ItemEnum);
19    let t = enum_t.ident;
20    let arms = enum_t
21        .variants
22        .iter()
23        .map(|branch| -> proc_macro2::TokenStream {
24            let b = &branch.ident;
25            let lit = b.to_string();
26            parse_quote!(#lit => Ok(#t::#b))
27        });
28    let tokens = quote! {
29        impl TryFrom<&crate::object::Object> for #t {
30            type Error = crate::ObjectValueError;
31            fn try_from(object: &crate::object::Object) -> Result<Self, Self::Error> {
32                match object.name()?.as_str() {
33                    #( #arms, )*
34                    _ => Err(crate::ObjectValueError::GraphicsOperationSchemaError),
35                }
36            }
37        }
38
39        impl<'a, 'b> TryFrom<crate::object::ObjectWithResolver<'a, 'b>> for #t {
40            type Error = crate::ObjectValueError;
41            fn try_from(object: crate::object::ObjectWithResolver<'a, 'b>) -> Result<Self, Self::Error> {
42                object.into_object().try_into()
43            }
44        }
45
46        impl<'b> crate::graphics::ConvertFromObject<'b> for #t {
47            fn convert_from_object(objects: &'b mut Vec<crate::object::Object>) -> Result<Self, crate::ObjectValueError> {
48                let o = objects.pop().unwrap();
49                #t::try_from(&o).map_err(|_| crate::ObjectValueError::GraphicsOperationSchemaError)
50            }
51        }
52    };
53    // println!("{}", tokens);
54    tokens.into()
55}
56
57/// impl TryFrom trait for enum that convert Object::Int to enum variant
58#[proc_macro_derive(TryFromIntObject)]
59pub fn try_from_int_object(input: TokenStream) -> TokenStream {
60    let enum_t = parse_macro_input!(input as ItemEnum);
61    let t = enum_t.ident;
62    let arms = enum_t
63        .variants
64        .iter()
65        .map(|branch| -> proc_macro2::TokenStream {
66            let Some((
67                _,
68                Expr::Lit(ExprLit {
69                    lit: Lit::Int(ref lit),
70                    ..
71                }),
72            )) = branch.discriminant
73            else {
74                panic!("Enum discriminant must be literal");
75            };
76            let digit: i32 = lit.base10_parse().unwrap();
77            let b = &branch.ident;
78            parse_quote!( #digit=> Ok(#t::#b))
79        });
80    let tokens = quote! {
81        impl<'a, 'b> TryFrom<crate::object::ObjectWithResolver<'a, 'b>> for #t {
82            type Error = crate::ObjectValueError;
83            fn try_from(object: crate::object::ObjectWithResolver<'a, 'b>) -> Result<Self, Self::Error> {
84                 object.into_object().try_into()
85            }
86        }
87
88        impl TryFrom<&crate::object::Object> for #t {
89            type Error = crate::ObjectValueError;
90            fn try_from(object: &crate::object::Object) -> Result<Self, Self::Error> {
91                let n = object.int()?;
92                match n {
93                    #( #arms, )*
94                    _ => Err(crate::ObjectValueError::GraphicsOperationSchemaError),
95                }
96            }
97        }
98
99        impl<'b> crate::graphics::ConvertFromObject<'b> for #t {
100            fn convert_from_object(objects: &'b mut Vec<crate::object::Object>) -> Result<Self, crate::ObjectValueError> {
101                let o = objects.pop().unwrap();
102                #t::try_from(&o).map_err(|_| crate::ObjectValueError::GraphicsOperationSchemaError)
103            }
104        }
105    };
106    // println!("{}", tokens);
107    tokens.into()
108}
109
110/// derive macro to generate `TryFrom<&Object>` for bitflags struct type
111#[proc_macro_derive(TryFromIntObjectForBitflags)]
112pub fn try_from_int_object_for_bitflags(input: TokenStream) -> TokenStream {
113    let t = parse_macro_input!(input as ItemStruct);
114    let t = t.ident;
115    let tokens = quote! {
116        impl TryFrom<crate::object::ObjectWithResolver<'_, '_>> for #t {
117            type Error = crate::ObjectValueError;
118
119            fn try_from(object: crate::object::ObjectWithResolver<'_, '_>) -> Result<Self, Self::Error> {
120                let n = object.into_object().int()?;
121                Ok(<#t as bitflags::Flags>::from_bits_truncate(n as u32))
122            }
123        }
124    };
125    // println!("{}", tokens);
126    tokens.into()
127}
128
129#[proc_macro_derive(OperationParser, attributes(op_tag))]
130pub fn graphics_operation_parser(input: TokenStream) -> TokenStream {
131    let op_enum = parse_macro_input!(input as ItemEnum);
132    let new_arm = |s: &str, body: Expr| Arm {
133        pat: Pat::Lit(ExprLit {
134            attrs: vec![],
135            lit: Lit::Str(LitStr::new(s, Span::call_site())),
136        }),
137        guard: None,
138        body: body.into(),
139        comma: None,
140        attrs: vec![],
141        fat_arrow_token: Token![=>](Span::call_site()),
142    };
143
144    let mut arms = vec![];
145    for branch in op_enum.variants {
146        let mut convert_args: Vec<Expr> = vec![];
147        if !branch.fields.is_empty() {
148            if let Fields::Unnamed(FieldsUnnamed {
149                unnamed: fields, ..
150            }) = branch.fields
151            {
152                for f in fields {
153                    let t = f.ty;
154                    convert_args.push(
155                        parse_quote!( <#t as ConvertFromObject>::convert_from_object(operands)?),
156                    );
157                }
158            }
159        }
160        let op = branch.ident;
161        let op: Expr = parse_quote!(Operation::#op);
162        let mut s = None;
163        for attr in &branch.attrs {
164            if let Meta::List(ref list) = attr.meta {
165                if list.path.is_ident("op_tag") {
166                    let tokens: TokenStream = list.tokens.clone().into();
167                    if let ExprLit {
168                        lit: Lit::Str(lit), ..
169                    } = parse_macro_input!(tokens as ExprLit)
170                    {
171                        s = Some(lit.value());
172                        break;
173                    }
174                }
175            }
176        }
177
178        arms.push(new_arm(
179            &s.expect("op_tag not defined"),
180            match convert_args.len() {
181                0 => parse_quote!(Some(#op)),
182                1 => parse_quote!(Some(#op(#(#convert_args),*))),
183                _ => {
184                    let mut save_to_vars = vec![];
185                    let mut vars = vec![];
186                    for (idx, arg) in convert_args.into_iter().enumerate() {
187                        // store arg result in variable _arg_idx
188                        let var = Ident::new(&(format!("_arg_{}", idx)), Span::call_site());
189                        vars.push(var.clone());
190                        save_to_vars.push(quote!( let #var = #arg; ));
191                    }
192                    save_to_vars.reverse();
193                    parse_quote!( {
194                        #( #save_to_vars )*
195                        Some(#op(#(#vars),*))
196                    })
197                }
198            },
199        ));
200    }
201    // "w" => Operation::SetLineWidth(f32::convert_from_object(operands)?)
202    // arms.push(arm("w", {
203    //     let op = operation_value("SetLineWidth");
204    //     let convert_from_object = convert_from_object();
205    //     parse_quote!( #op(f32::#convert_from_object(operands)?) )
206    // }));
207
208    let tokens = quote! {
209        fn create_operation(op: &str, operands: &mut Vec<crate::object::Object>) -> Result<Option<Operation>, crate::ObjectValueError> {
210            Ok(match op {
211                #( #arms, )*
212                _ => None,
213            })
214        }
215    };
216    // println!("{}", tokens);
217    tokens.into()
218}
219
220mod pdf_object_impl;
221
222#[proc_macro_attribute]
223pub fn pdf_object(attr: TokenStream, item: TokenStream) -> TokenStream {
224    pdf_object_impl::pdf_object(attr, item)
225}