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#[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 tokens.into()
55}
56
57#[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 tokens.into()
108}
109
110#[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 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),
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 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 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 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}