#![recursion_limit="128"]
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::{TokenStream};
use proc_macro2::{TokenStream as TokenStream2, Span};
use syn::{*, punctuated::Punctuated, token::Where};
type SynStream = TokenStream2;
#[proc_macro_derive(Object, attributes(pdf))]
pub fn object(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
impl_object(&ast)
}
#[proc_macro_derive(ObjectWrite, attributes(pdf))]
pub fn objectwrite(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
impl_objectwrite(&ast)
}
#[proc_macro_derive(DeepClone, attributes(pdf))]
pub fn deepclone(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
impl_deepclone(&ast)
}
#[derive(Default)]
struct FieldAttrs {
key: Option<LitStr>,
default: Option<LitStr>,
name: Option<LitStr>,
skip: bool,
other: bool,
indirect: bool,
}
impl FieldAttrs {
fn new() -> FieldAttrs {
FieldAttrs {
key: None,
default: None,
name: None,
skip: false,
other: false,
indirect: false,
}
}
fn key(&self) -> &LitStr {
self.key.as_ref().expect("no 'key' in field attributes")
}
fn default(&self) -> Option<Expr> {
self.default.as_ref().map(|s| parse_str(&s.value()).expect("can't parse `default` as EXPR"))
}
fn parse(list: &[Attribute]) -> FieldAttrs {
let mut attrs = FieldAttrs::new();
for attr in list.iter().filter(|attr| attr.path.is_ident("pdf")) {
let list = match attr.parse_meta() {
Ok(Meta::List(list)) => list,
Ok(_) => panic!("only #[pdf(attrs...)] is allowed"),
Err(e) => panic!("can't parse meta attributes: {}", e)
};
for meta in list.nested.iter() {
match *meta {
NestedMeta::Meta(Meta::NameValue(MetaNameValue { ref path, lit: Lit::Str(ref value), ..})) => {
if path.is_ident("key") {
attrs.key = Some(value.clone());
} else if path.is_ident("default") {
attrs.default = Some(value.clone());
} else if path.is_ident("name") {
attrs.name = Some(value.clone());
} else {
panic!("unsupported key {}", path.segments.iter().map(|s| s.ident.to_string()).collect::<Vec<String>>().join("::"))
}
},
NestedMeta::Meta(Meta::Path(ref path)) if path.is_ident("skip") => attrs.skip = true,
NestedMeta::Meta(Meta::Path(ref path)) if path.is_ident("other") => attrs.other = true,
NestedMeta::Meta(Meta::Path(ref path)) if path.is_ident("indirect") => attrs.indirect = true,
_ => panic!(r#"Derive error - Supported derive attributes: `key="Key"`, `default="some code", skip, other, indirect`."#)
}
}
}
attrs
}
}
#[derive(Default, Debug)]
struct GlobalAttrs {
checks: Vec<(String, String)>,
type_name: Option<String>,
type_required: bool,
is_stream: bool
}
impl GlobalAttrs {
fn from_ast(ast: &DeriveInput) -> GlobalAttrs {
let mut attrs = GlobalAttrs::default();
for attr in ast.attrs.iter().filter(|attr| attr.path.is_ident("pdf")) {
let list = match attr.parse_meta() {
Ok(Meta::List(list)) => list,
Ok(_) => panic!("only #[pdf(attrs...)] is allowed"),
Err(e) => panic!("can't parse meta attributes: {}", e)
};
for meta in list.nested.iter() {
match *meta {
NestedMeta::Meta(Meta::NameValue(MetaNameValue { ref path, ref lit, ..})) => {
if path.is_ident("Type") {
match lit {
Lit::Str(ref value) => {
let mut value = value.value();
attrs.type_required = if value.ends_with('?') {
value.pop(); false
} else {
true
};
attrs.type_name = Some(value);
},
_ => panic!("Value of 'Type' attribute must be a String."),
}
} else {
match lit {
Lit::Str(ref value) => attrs.checks.push((path.segments.iter().map(|s| s.ident.to_string()).collect::<Vec<String>>().join("::"), value.value())),
_ => panic!("Other checks must have RHS String."),
}
}
},
NestedMeta::Meta(Meta::Path(ref path)) if path.is_ident("is_stream") => attrs.is_stream = true,
_ => {}
}
}
}
attrs
}
}
fn impl_object(ast: &DeriveInput) -> TokenStream {
let attrs = GlobalAttrs::from_ast(ast);
match (attrs.is_stream, &ast.data) {
(true, Data::Struct(ref data)) => impl_object_for_stream(ast, &data.fields).into(),
(false, Data::Struct(ref data)) => impl_object_for_struct(ast, &data.fields).into(),
(true, Data::Enum(ref variants)) => impl_enum_from_stream(ast, variants, &attrs).into(),
(false, Data::Enum(ref variants)) => impl_object_for_enum(ast, variants).into(),
(_, _) => unimplemented!()
}
}
fn impl_objectwrite(ast: &DeriveInput) -> TokenStream {
let attrs = GlobalAttrs::from_ast(ast);
match (attrs.is_stream, &ast.data) {
(false, Data::Struct(ref data)) => impl_objectwrite_for_struct(ast, &data.fields).into(),
(false, Data::Enum(ref variants)) => impl_objectwrite_for_enum(ast, variants).into(),
(_, _) => unimplemented!()
}
}
fn impl_deepclone(ast: &DeriveInput) -> TokenStream {
let attrs = GlobalAttrs::from_ast(ast);
match &ast.data {
Data::Struct(ref data) => impl_deepclone_for_struct(ast, &data.fields).into(),
Data::Enum(ref variants) => impl_deepclone_for_enum(ast, variants).into(),
_ => unimplemented!()
}
}
fn enum_pairs(ast: &DeriveInput, data: &DataEnum) -> (Vec<(String, TokenStream2)>, Option<TokenStream2>) {
let id = &ast.ident;
let mut pairs = Vec::with_capacity(data.variants.len());
let mut other = None;
for var in data.variants.iter() {
let attrs = FieldAttrs::parse(&var.attrs);
let var_ident = &var.ident;
let name = attrs
.name
.map(|lit| lit.value())
.unwrap_or_else(|| var_ident.to_string());
if attrs.other {
assert!(other.is_none(), "only one 'other' variant is allowed in a name enum");
match &var.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {}
_ => {
panic!(
"the 'other' variant in a name enum should have exactly one unnamed field",
);
}
}
other = Some(quote! { #id::#var_ident });
} else {
pairs.push((name, quote! { #id::#var_ident }));
}
}
(pairs, other)
}
fn impl_object_for_enum(ast: &DeriveInput, data: &DataEnum) -> SynStream {
let id = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let int_count = data.variants.iter().filter(|var| var.discriminant.is_some()).count();
if int_count > 0 {
assert_eq!(int_count, data.variants.len(), "either none or all variants can have a descriminant");
let parts = data.variants.iter().map(|var| {
if let Some((_, ref expr)) = var.discriminant {
let var_ident = &var.ident;
let pat = Pat::Lit(PatLit { expr: Box::new(expr.clone()), attrs: vec![] });
quote! {
#pat => Ok(#id::#var_ident)
}
} else {
panic!()
}
});
quote! {
impl #impl_generics pdf::object::Object for #id #ty_generics #where_clause {
fn from_primitive(p: pdf::primitive::Primitive, _resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
match p {
pdf::primitive::Primitive::Integer(i) => {
match i {
#( #parts, )*
_ => Err(pdf::error::PdfError::UnknownVariant { id: stringify!(#id), name: i.to_string() })
}
}
_ => Err(pdf::error::PdfError::UnexpectedPrimitive { expected: "Integer", found: p.get_debug_name() }),
}
}
}
}
} else {
let (pairs, other) = enum_pairs(ast, data);
let mut parts: Vec<_> = pairs
.iter()
.map(|(name, var)| {
quote! {
#name => Ok(#var)
}
})
.collect();
if let Some(other_tokens) = other {
parts.push(quote! {
s => Ok(#other_tokens(s.to_string()))
});
} else {
parts.push(quote! {
s => Err(pdf::error::PdfError::UnknownVariant { id: stringify!(#id), name: s.to_string() })
});
}
quote! {
impl #impl_generics pdf::object::Object for #id #ty_generics #where_clause {
fn from_primitive(p: pdf::primitive::Primitive, _resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
match p {
pdf::primitive::Primitive::Name(name) => {
match name.as_str() {
#( #parts, )*
}
}
_ => Err(pdf::error::PdfError::UnexpectedPrimitive { expected: "Name", found: p.get_debug_name() }),
}
}
}
}
}
}
fn impl_objectwrite_for_enum(ast: &DeriveInput, data: &DataEnum) -> SynStream {
let id = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let int_count = data.variants.iter().filter(|var| var.discriminant.is_some()).count();
if int_count > 0 {
assert_eq!(int_count, data.variants.len(), "either none or all variants can have a descriminant");
let parts = data.variants.iter().map(|var| {
if let Some((_, ref expr)) = var.discriminant {
let var_ident = &var.ident;
quote! {
#id::#var_ident => Ok(Primitive::Integer(#expr))
}
} else {
panic!()
}
});
quote! {
impl #impl_generics pdf::object::ObjectWrite for #id #ty_generics #where_clause {
fn to_primitive(&self, update: &mut impl pdf::object::Updater) -> Result<Primitive> {
match *self {
#( #parts, )*
}
}
}
}
} else {
let (pairs, other) = enum_pairs(ast, data);
let mut ser_code: Vec<_> = pairs
.iter()
.map(|(name, var)| {
quote! {
#var => #name
}
})
.collect();
if let Some(other_tokens) = other {
ser_code.push(quote! {
#other_tokens(ref name) => name.as_str()
});
}
quote! {
impl #impl_generics pdf::object::ObjectWrite for #id #ty_generics #where_clause {
fn to_primitive(&self, update: &mut impl pdf::object::Updater) -> Result<Primitive> {
let name = match *self {
#( #ser_code, )*
};
Ok(Primitive::Name(name.into()))
}
}
}
}
}
fn impl_deepclone_for_enum(ast: &DeriveInput, data: &DataEnum) -> SynStream {
let id = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let parts = data.variants.iter().map(|var| {
let var_ident = &var.ident;
match var.fields {
Fields::Unnamed(ref fields) => {
let labels: Vec<Ident> = fields.unnamed.iter().enumerate().map(|(i, f)| {
Ident::new(&format!("f_{i}"), Span::mixed_site())
}).collect();
quote! {
#id::#var_ident( #( ref #labels, )* ) => Ok(#id::#var_ident( #( #labels.deep_clone(cloner)? ),* ))
}
}
Fields::Named(ref fields) => {
let names: Vec<_> = fields.named.iter().map(|f| f.ident.as_ref().unwrap()).collect();
quote! {
#id::#var_ident { #( ref #names ),* } => Ok(#id::#var_ident { #( #names: #names.deep_clone(cloner)? ),* })
}
}
Fields::Unit => {
quote! {
#id::#var_ident => Ok(#id::#var_ident)
}
}
}
});
quote! {
impl #impl_generics pdf::object::DeepClone for #id #ty_generics #where_clause {
fn deep_clone(&self, cloner: &mut impl pdf::object::Cloner) -> Result<Self> {
match *self {
#( #parts, )*
}
}
}
}
}
fn impl_enum_from_stream(ast: &DeriveInput, data: &DataEnum, attrs: &GlobalAttrs) -> SynStream {
let id = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let ty_check = match (&attrs.type_name, attrs.type_required) {
(Some(ref ty), required) => quote! {
stream.info.expect(stringify!(#id), "Type", #ty, #required)?;
},
(None, _) => quote!{}
};
let variants_code: Vec<_> = data.variants.iter().map(|var| {
let attrs = FieldAttrs::parse(&var.attrs);
let inner_ty = match var.fields {
Fields::Unnamed(ref fields) => {
assert_eq!(fields.unnamed.len(), 1, "all variants in a stream enum have to have exactly one unnamed field");
fields.unnamed.first().unwrap().ty.clone()
},
_ => panic!("all variants in a stream enum have to have exactly one unnamed field")
};
let name = attrs.name.map(|lit| lit.value()).unwrap_or_else(|| var.ident.to_string());
let variant_ident = &var.ident;
quote! {
#name => Ok(#id::#variant_ident ( #inner_ty::from_primitive(pdf::primitive::Primitive::Stream(stream), resolve)?))
}
}).collect();
quote! {
impl #impl_generics pdf::object::Object for #id #ty_generics #where_clause {
fn from_primitive(p: pdf::primitive::Primitive, resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
let mut stream = PdfStream::from_primitive(p, resolve)?;
#ty_check
let subty = stream.info.get("Subtype")
.ok_or(pdf::error::PdfError::MissingEntry { typ: stringify!(#id), field: "Subtype".into()})?
.as_name()?;
match subty {
#( #variants_code, )*
s => Err(pdf::error::PdfError::UnknownVariant { id: stringify!(#id), name: s.into() })
}
}
}
}
}
fn is_option(f: &Field) -> Option<Type> {
match f.ty {
Type::Path(ref p) => {
let first = p.path.segments.first().unwrap();
match first {
PathSegment { ident, arguments: PathArguments::AngleBracketed(args) } if ident == "Option" => {
match args.args.first().unwrap() {
GenericArgument::Type(t) => Some(t.clone()),
_ => panic!()
}
}
_ => None
}
}
_ => None
}
}
fn impl_object_for_struct(ast: &DeriveInput, fields: &Fields) -> SynStream {
let id = &ast.ident;
let mut generics = ast.generics.clone();
for g in generics.params.iter_mut() {
if let GenericParam::Type(p) = g {
p.bounds.push(
parse_quote!(pdf::object::Object)
);
}
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let attrs = GlobalAttrs::from_ast(ast);
let typ = id.to_string();
let let_parts = fields.iter().map(|field| {
let name = &field.ident;
let attrs = FieldAttrs::parse(&field.attrs);
if attrs.skip {
return quote! {}
}
if attrs.other {
return quote! {
let #name = dict;
};
}
let key = attrs.key();
let ty = field.ty.clone();
if let Some(ref default) = attrs.default() {
quote! {
let #name = {
let primitive: Option<pdf::primitive::Primitive>
= dict.remove(#key);
let x: #ty = match primitive {
Some(primitive) => <#ty as pdf::object::Object>::from_primitive(primitive, resolve).map_err(|e|
pdf::error::PdfError::FromPrimitive {
typ: #typ,
field: stringify!(#name),
source: Box::new(e)
})?,
None => #default,
};
x
};
}
} else {
quote! {
let #name = {
match dict.remove(#key) {
Some(primitive) =>
match <#ty as pdf::object::Object>::from_primitive(primitive, resolve) {
Ok(obj) => obj,
Err(e) => return Err(pdf::error::PdfError::FromPrimitive {
typ: stringify!(#ty),
field: stringify!(#name),
source: Box::new(e)
})
}
None => match <#ty as pdf::object::Object>::from_primitive(pdf::primitive::Primitive::Null, resolve) {
Ok(obj) => obj,
Err(_) => return Err(pdf::error::PdfError::MissingEntry {
typ: stringify!(#ty),
field: String::from(stringify!(#name)),
})
},
}
};
}
}
});
let field_parts = fields.iter().map(|field| {
let name = &field.ident;
quote! { #name: #name, }
});
let checks: Vec<_> = attrs.checks.iter().map(|(key, val)|
quote! {
dict.expect(#typ, #key, #val, true)?;
}
).collect();
let ty_check = match (&attrs.type_name, attrs.type_required) {
(Some(ref ty), required) => quote! {
dict.expect(#typ, "Type", #ty, #required)?;
},
(None, _) => quote!{}
};
quote! {
impl #impl_generics pdf::object::FromDict for #id #ty_generics #where_clause {
fn from_dict(mut dict: pdf::primitive::Dictionary, resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
#ty_check
#( #checks )*
#( #let_parts )*
Ok(#id {
#( #field_parts )*
})
}
}
impl #impl_generics pdf::object::Object for #id #ty_generics #where_clause {
fn from_primitive(p: pdf::primitive::Primitive, resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
let dict = pdf::primitive::Dictionary::from_primitive(p, resolve)?;
<Self as pdf::object::FromDict>::from_dict(dict, resolve)
}
}
}
}
fn impl_objectwrite_for_struct(ast: &DeriveInput, fields: &Fields) -> SynStream {
let id = &ast.ident;
let mut generics = ast.generics.clone();
for g in generics.params.iter_mut() {
if let GenericParam::Type(p) = g {
p.bounds.push(
parse_quote!(pdf::object::ObjectWrite)
);
}
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let attrs = GlobalAttrs::from_ast(ast);
let parts: Vec<_> = fields.iter()
.map(|field| {
(field.ident.clone(), FieldAttrs::parse(&field.attrs), is_option(field))
}).collect();
let fields_ser = parts.iter()
.map( |(field, attrs, _opt)|
if attrs.skip | attrs.other {
quote!()
} else {
let key = attrs.key();
let tr = if attrs.indirect {
quote! {
match val {
pdf::primitive::Primitive::Reference(r) => val,
p => updater.create(p)?.into(),
}
}
} else {
quote! { val }
};
quote! {
let val = pdf::object::ObjectWrite::to_primitive(&self.#field, updater)?;
if !matches!(val, pdf::primitive::Primitive::Null) {
let val2 = #tr;
dict.insert(#key, val2);
}
}
}
);
let checks_code = attrs.checks.iter().map(|(key, val)|
quote! {
dict.insert(#key, pdf::primitive::Primitive::Name(#val.into()));
}
);
let pdf_type = match attrs.type_name {
Some(ref name) => quote! {
dict.insert("Type", pdf::primitive::Primitive::Name(#name.into()));
},
None => quote! {}
};
quote! {
impl #impl_generics pdf::object::ObjectWrite for #id #ty_generics #where_clause {
fn to_primitive(&self, update: &mut impl pdf::object::Updater) -> Result<pdf::primitive::Primitive> {
pdf::object::ToDict::to_dict(self, update).map(pdf::primitive::Primitive::Dictionary)
}
}
impl #impl_generics pdf::object::ToDict for #id #ty_generics #where_clause {
fn to_dict(&self, updater: &mut impl pdf::object::Updater) -> Result<pdf::primitive::Dictionary> {
let mut dict = pdf::primitive::Dictionary::new();
#pdf_type
#( #checks_code )*
#(#fields_ser)*
Ok(dict)
}
}
}
}
fn impl_deepclone_for_struct(ast: &DeriveInput, fields: &Fields) -> SynStream {
let id = &ast.ident;
let mut generics = ast.generics.clone();
for g in generics.params.iter_mut() {
if let GenericParam::Type(p) = g {
p.bounds.push(
parse_quote!(pdf::object::DeepClone)
);
}
}
let (impl_generics, mut ty_generics, where_clause) = generics.split_for_impl();
let parts: Vec<_> = fields.iter()
.map(|field| {
(field.ident.clone(), is_option(field))
}).collect();
let field_parts = parts.iter()
.map( |(field, _opt)|
{
quote! {
#field: self.#field.deep_clone(cloner)?,
}
}
);
quote! {
impl #impl_generics pdf::object::DeepClone for #id #ty_generics #where_clause {
fn deep_clone(&self, cloner: &mut impl pdf::object::Cloner) -> Result<Self> {
Ok(#id {
#( #field_parts )*
})
}
}
}
}
fn impl_object_for_stream(ast: &DeriveInput, fields: &Fields) -> SynStream {
let id = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let info_ty = fields.iter()
.filter_map(|field| {
if let Some(ident) = field.ident.as_ref() {
if ident == "info" {
Some(field.ty.clone())
} else {
None
}
} else {
None
}
}).next().unwrap();
quote! {
impl #impl_generics pdf::object::Object for #id #ty_generics #where_clause {
fn from_primitive(p: pdf::primitive::Primitive, resolve: &impl pdf::object::Resolve) -> pdf::error::Result<Self> {
let pdf::primitive::PdfStream {info, data}
= p.to_stream(resolve)?;
Ok(#id {
info: <#info_ty as pdf::object::Object>::from_primitive(pdf::primitive::Primitive::Dictionary (info), resolve)?,
data: data,
})
}
}
}
}