1extern crate proc_macro;
2use crate::proc_macro::TokenStream;
3use quote::quote;
4use syn::parse::{Parse, ParseStream};
5use syn::punctuated::Punctuated;
6use syn::token::Comma;
7use syn::token::Eq;
8
9use syn::Lit;
10use syn::Path;
11use syn::{Attribute, Data, DeriveInput, Field, Fields, Ident, Result as SynResult};
12
13const NAMED_STRUCT_ONLY_ERR: &str = "Derive macro only implemented for named structs";
14
15const INVALID_ATTR_NAME: &str = "Invalid struct attribute. Accepted name is only 'path'";
16const INVALID_ATTR_COUNT: &str =
17 "Invalid struct attributes. Only use 'path' attribute in struct once";
18
19const INVALID_FIELD_ATTR_NAME: &str =
20 "Invalid field attribute. Accepted attribute names are 'name', and 'hash'";
21const INVALID_FIELD_ATTR_COUNT: &str =
22 "Invalid field attributes. Only use 'name' or 'hash' attribute in field once";
23
24#[proc_macro_derive(Prc, attributes(prc))]
25pub fn prc_derive(input: TokenStream) -> TokenStream {
26 match derive_or_error(input) {
27 Err(err) => err.to_compile_error().into(),
28 Ok(result) => result,
29 }
30}
31
32fn derive_or_error(input: TokenStream) -> SynResult<TokenStream> {
33 let input: DeriveInput = syn::parse(input)?;
34 let ident = input.ident;
35
36 let attrs = parse_struct_attributes(&input.attrs)?;
37
38 match input.data {
39 Data::Struct(data_struct) => match &data_struct.fields {
40 Fields::Named(fields) => derive_named_struct(ident, &attrs, &fields.named),
41 Fields::Unnamed(..) => panic!("{}", NAMED_STRUCT_ONLY_ERR),
42 Fields::Unit => panic!("{}", NAMED_STRUCT_ONLY_ERR),
43 },
44 _ => panic!("{}", NAMED_STRUCT_ONLY_ERR),
45 }
46}
47
48#[derive(Default)]
49struct MainAttributes {
50 path: Option<Path>,
51}
52
53enum MainAttribute {
54 Path(Path),
55}
56
57enum FieldAttribute {
58 Name(Lit),
61 Hash(Lit),
62}
63
64fn parse_struct_attributes(attrs: &[Attribute]) -> SynResult<MainAttributes> {
65 let mut attributes = MainAttributes::default();
66
67 attrs
68 .iter()
69 .filter(|attr| attr.path.is_ident("prc"))
70 .try_for_each(|attr| {
71 let attr_kind: MainAttribute = attr.parse_args()?;
72 match attr_kind {
73 MainAttribute::Path(path) => {
74 if attributes.path.is_some() {
75 panic!("{}", INVALID_ATTR_COUNT);
76 } else {
77 attributes.path = Some(path);
78 }
79 }
80 }
81
82 SynResult::Ok(())
83 })?;
84
85 Ok(attributes)
86}
87
88impl Parse for MainAttribute {
89 fn parse(input: ParseStream) -> SynResult<Self> {
90 let key: Ident = input.parse()?;
91 let struct_attr = match key.to_string().as_ref() {
92 "path" => {
93 let _eq: Eq = input.parse()?;
94 MainAttribute::Path(input.parse()?)
95 }
96 _ => panic!("{}", INVALID_ATTR_NAME),
97 };
98
99 SynResult::Ok(struct_attr)
100 }
101}
102
103impl Parse for FieldAttribute {
104 fn parse(input: ParseStream) -> SynResult<Self> {
105 let key: Ident = input.parse()?;
106 match key.to_string().as_ref() {
107 "name" => {
108 let _eq: Eq = input.parse()?;
109 Ok(FieldAttribute::Name(input.parse()?))
110 }
111 "hash" => {
112 let _eq: Eq = input.parse()?;
113 Ok(FieldAttribute::Hash(input.parse()?))
114 }
115 _ => Err(input.error(INVALID_FIELD_ATTR_NAME)),
117 }
118 }
119}
120
121fn derive_named_struct(
122 ident: Ident,
123 attrs: &MainAttributes,
124 fields: &Punctuated<Field, Comma>,
125) -> SynResult<TokenStream> {
126 let path = attrs
127 .path
128 .as_ref()
129 .map(|some_path| quote!(#some_path))
130 .unwrap_or(quote!(::prc));
131
132 let names = fields
133 .iter()
134 .map(|field| {
135 let attrs = field
136 .attrs
137 .iter()
138 .filter(|attr| attr.path.is_ident("prc"))
139 .map(|attr| attr.parse_args())
140 .collect::<Result<Vec<_>, _>>()?;
141
142 if attrs.len() > 1 {
143 panic!("{}", INVALID_FIELD_ATTR_COUNT);
144 }
145
146 let ident = field.ident.as_ref().unwrap();
147 let ident_string = ident.to_string();
148
149 let hash = match attrs.get(0) {
150 Some(FieldAttribute::Hash(hash)) => quote!(#path::hash40::Hash40(#hash)),
151 Some(FieldAttribute::Name(name)) => quote!(#path::hash40::hash40(#name)),
152 None => quote!(#path::hash40::hash40(#ident_string)),
153 };
154
155 Ok((ident, hash))
156 })
157 .collect::<SynResult<Vec<_>>>()?;
158
159 let struct_names = names.iter().map(|name| name.0);
160 let hashes = names.iter().map(|name| &name.1);
161
162 Ok(quote! {
163 impl Prc for #ident {
164 fn read_param<R: ::std::io::Read + ::std::io::Seek>(reader: &mut R, offsets: #path::prc_trait::FileOffsets) -> #path::prc_trait::Result<Self> {
165 let data = #path::prc_trait::StructData::from_stream(reader)?;
166 Ok(Self {
167 #(
168 #struct_names: Prc::read_from_struct(reader, #hashes, offsets, data)?,
169 )*
170 })
171 }
172 }
173 }
174 .into())
175}