1#![allow(unknown_lints)]
28#![deny(
29 clippy::await_holding_lock,
30 clippy::borrow_as_ptr,
31 clippy::branches_sharing_code,
32 clippy::cast_lossless,
33 clippy::clippy::collection_is_never_read,
34 clippy::cloned_instead_of_copied,
35 clippy::cognitive_complexity,
36 clippy::create_dir,
37 clippy::deref_by_slicing,
38 clippy::derivable_impls,
39 clippy::derive_partial_eq_without_eq,
40 clippy::equatable_if_let,
41 clippy::exhaustive_structs,
42 clippy::expect_used,
43 clippy::expl_impl_clone_on_copy,
44 clippy::explicit_deref_methods,
45 clippy::explicit_into_iter_loop,
46 clippy::explicit_iter_loop,
47 clippy::filetype_is_file,
48 clippy::flat_map_option,
49 clippy::format_push_string,
50 clippy::fn_params_excessive_bools,
51 clippy::future_not_send,
52 clippy::get_unwrap,
53 clippy::implicit_clone,
54 clippy::if_then_some_else_none,
55 clippy::impl_trait_in_params,
56 clippy::implicit_clone,
57 clippy::inefficient_to_string,
58 clippy::inherent_to_string,
59 clippy::iter_not_returning_iterator,
60 clippy::large_types_passed_by_value,
61 clippy::large_include_file,
62 clippy::let_and_return,
63 clippy::manual_assert,
64 clippy::manual_ok_or,
65 clippy::manual_split_once,
66 clippy::manual_let_else,
67 clippy::manual_string_new,
68 clippy::map_flatten,
69 clippy::map_unwrap_or,
70 clippy::missing_enforced_import_renames,
71 clippy::missing_assert_message,
72 clippy::missing_const_for_fn,
73 clippy::must_use_candidate,
74 clippy::mut_mut,
75 clippy::needless_for_each,
76 clippy::needless_option_as_deref,
77 clippy::needless_pass_by_value,
78 clippy::needless_collect,
79 clippy::needless_continue,
80 clippy::non_send_fields_in_send_ty,
81 clippy::nonstandard_macro_braces,
82 clippy::option_if_let_else,
83 clippy::option_option,
84 clippy::rc_mutex,
85 clippy::redundant_else,
86 clippy::same_name_method,
87 clippy::semicolon_if_nothing_returned,
88 clippy::str_to_string,
89 clippy::string_to_string,
90 clippy::too_many_lines,
91 clippy::trivially_copy_pass_by_ref,
92 clippy::trivial_regex,
93 clippy::try_err,
94 clippy::unnested_or_patterns,
95 clippy::unused_async,
96 clippy::unwrap_or_else_default,
97 clippy::useless_let_if_seq,
98 bad_style,
99 clashing_extern_declarations,
100 dead_code,
101 deprecated,
102 explicit_outlives_requirements,
103 improper_ctypes,
104 invalid_value,
105 missing_copy_implementations,
106 missing_debug_implementations,
107 mutable_transmutes,
108 no_mangle_generic_items,
109 non_shorthand_field_patterns,
110 overflowing_literals,
111 path_statements,
112 patterns_in_fns_without_body,
113 private_in_public,
114 trivial_bounds,
115 trivial_casts,
116 trivial_numeric_casts,
117 type_alias_bounds,
118 unconditional_recursion,
119 unreachable_pub,
120 unsafe_code,
121 unstable_features,
122 unused,
123 unused_allocation,
124 unused_comparisons,
125 unused_import_braces,
126 unused_parens,
127 unused_qualifications,
128 while_true,
129 missing_docs
130)]
131#![warn(clippy::exhaustive_enums)]
132#![allow(unused_attributes, clippy::derive_partial_eq_without_eq, clippy::box_default)]
133#![allow(clippy::expect_used)]
136
137use asset_container::AssetFlags;
138use proc_macro::TokenStream;
139use proc_macro2::Ident;
140use quote::quote;
141use structmeta::{NameArgs, StructMeta};
142use syn::{parse_macro_input, Attribute, DataEnum, DataStruct, DeriveInput, Type};
143
144#[derive(Debug, StructMeta)]
145struct TypeOpts {
146 lazy: bool,
147 #[struct_meta(name = "asset")]
148 ty: NameArgs<Type>,
149}
150
151#[proc_macro_derive(AssetManager, attributes(asset_managers, asset))]
153pub fn derive_asset_container(input: TokenStream) -> TokenStream {
154 let ast = parse_macro_input!(input as DeriveInput);
156
157 let name = &ast.ident;
159
160 let opts = ast
162 .attrs
163 .iter()
164 .find(|attr| attr.path().is_ident("asset"))
165 .map(|attribute| attribute.parse_args::<TypeOpts>().expect("invalid attribute arguments"))
166 .expect("no asset attribute");
167
168 match ast.data {
169 syn::Data::Struct(ref data) => impl_struct(name, data, opts),
170 syn::Data::Enum(ref data) => impl_enum(name, data, opts),
171 _ => panic!("Only structs and enums can derive the Assets trait."),
172 }
173}
174
175fn has_skip(attr: &[Attribute]) -> bool {
176 attr
177 .iter()
178 .find(|attr| attr.path().is_ident("asset"))
179 .map_or(false, |attr| {
180 let ident = attr.parse_args::<Ident>().expect("invalid attribute arguments");
181 ident == "skip"
182 })
183}
184
185fn impl_struct(name: &Ident, data: &DataStruct, opts: TypeOpts) -> TokenStream {
186 let fields = &data.fields;
187 let asset_fields = fields
189 .iter()
190 .filter(|field| field.ty == opts.ty.args)
191 .filter(|field| !has_skip(&field.attrs))
192 .map(|field| {
193 field
194 .ident
195 .as_ref()
196 .map_or_else(|| panic!("Unnamed fields are not supported."), |ident| ident.clone())
197 })
198 .collect::<Vec<Ident>>();
199
200 let inner_managers = fields
202 .iter()
203 .filter(|field| field.ty != opts.ty.args)
204 .filter(|field| !has_skip(&field.attrs))
205 .map(|field| {
206 field
207 .ident
208 .as_ref()
209 .map_or_else(|| panic!("Unnamed fields are not supported."), |ident| ident.clone())
210 })
211 .collect::<Vec<Ident>>();
212 let flags = if opts.lazy {
213 AssetFlags::Lazy
214 } else {
215 AssetFlags::empty()
216 }
217 .bits();
218 let flags = quote! {#flags};
219 let asset_type = opts.ty.args;
220
221 let output = quote! {
223 impl asset_container::AssetManager for #name {
224 type Asset = #asset_type;
225
226 fn set_baseurl(&self, baseurl: &std::path::Path) {
227 use asset_container::Asset;
228 #(self.#asset_fields.update_baseurl(baseurl);)*
229 #(self.#inner_managers.set_baseurl(baseurl);)*
230 }
231
232 fn assets(&self) -> asset_container::Assets<#asset_type> {
233 let mut assets = asset_container::Assets::new(vec![],self.get_asset_flags());
234 #(assets.push(&self.#asset_fields);)*
235 #(assets.extend(self.#inner_managers.assets());)*
236 assets
237 }
238
239 fn get_asset_flags(&self) -> u32 {
240 #flags
241 }
242 }
243 };
244 TokenStream::from(output)
245}
246
247fn impl_enum(name: &Ident, data: &DataEnum, opts: TypeOpts) -> TokenStream {
248 let variants = &data.variants;
249
250 let asset_variants = variants
251 .iter()
252 .filter(|v| v.fields.iter().any(|f| f.ty == opts.ty.args))
253 .filter(|field| !has_skip(&field.attrs))
254 .map(|field| field.ident.clone())
255 .collect::<Vec<Ident>>();
256
257 let inner_managers = variants
258 .iter()
259 .filter(|v| v.fields.iter().any(|f| f.ty != opts.ty.args))
260 .filter(|field| !has_skip(&field.attrs))
261 .map(|field| field.ident.clone())
262 .collect::<Vec<Ident>>();
263
264 let flags = if opts.lazy {
265 AssetFlags::Lazy
266 } else {
267 AssetFlags::empty()
268 }
269 .bits();
270 let flags = quote! {#flags};
271 let asset_type = opts.ty.args;
272
273 let output = quote! {
275 impl asset_container::AssetManager for #name {
276 type Asset = #asset_type;
277
278 fn set_baseurl(&self, baseurl: &std::path::Path) {
279 use asset_container::Asset;
280 match self {
281 #(Self::#asset_variants(v) => {
282 v.update_baseurl(baseurl);
283 })*
284 #(Self::#inner_managers(v) => {
285 v.set_baseurl(baseurl);
286 })*
287 _ => {}
288 }
289 }
290
291 fn assets(&self) -> asset_container::Assets<#asset_type> {
292 let mut assets = asset_container::Assets::new(vec![],self.get_asset_flags());
293 match self {
294 #(Self::#asset_variants(v) => {
295 assets.push(v);
296 })*
297 #(Self::#inner_managers(v) => {
298 assets.extend(asset_container::AssetManager::assets(v));
299 })*
300 _ => {}
301 }
302 assets
303 }
304
305 fn get_asset_flags(&self) -> u32 {
306 #flags
307 }
308 }
309 };
310 TokenStream::from(output)
311}