savefile_derive/
lib.rs

1#![recursion_limit = "128"]
2#![allow(warnings)]
3#![allow(clippy::needless_borrowed_reference)]
4#![allow(clippy::match_like_matches_macro)]
5#![allow(clippy::bool_assert_comparison)]
6#![allow(clippy::bool_comparison)]
7#![allow(clippy::match_ref_pats)] // This one I'd really like to clean up some day
8#![allow(clippy::needless_late_init)]
9#![allow(clippy::len_zero)]
10#![allow(clippy::let_and_return)]
11#![allow(clippy::collapsible_match)]
12#![allow(clippy::single_match)]
13
14//! This crate allows automatic derivation of the Savefile-traits: Serialize, Deserialize, WithSchema, Packed and Introspect .
15//! The documentation for this is found in the Savefile crate documentation.
16
17extern crate proc_macro;
18extern crate proc_macro2;
19#[macro_use]
20extern crate quote;
21extern crate syn;
22#[macro_use]
23extern crate proc_macro_error2;
24
25use crate::savefile_abi::is_well_known;
26use common::{
27    check_is_remove, compile_time_check_reprc, compile_time_size, get_extra_where_clauses, parse_attr_tag,
28    path_to_string, FieldInfo,
29};
30use proc_macro2::TokenStream;
31use proc_macro2::{Span, TokenTree};
32use quote::ToTokens;
33use std::collections::{HashMap, HashSet};
34#[allow(unused_imports)]
35use std::iter::IntoIterator;
36use syn::__private::bool;
37use syn::parse::Parse;
38use syn::spanned::Spanned;
39use syn::token::Paren;
40use syn::Type::Tuple;
41use syn::{
42    Data, DeriveInput, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemTrait, Lifetime,
43    Pat, PathArguments, ReturnType, TraitItem, Type, TypeGenerics, TypeParamBound, TypeTuple, WherePredicate,
44};
45
46fn implement_fields_serialize(
47    field_infos: Vec<FieldInfo>,
48    implicit_self: bool,
49    index: bool,
50) -> (TokenStream, Vec<TokenStream>) {
51    let mut output = Vec::new();
52
53    let defspan = proc_macro2::Span::call_site();
54    let span = proc_macro2::Span::call_site();
55    let local_serializer = quote_spanned! { defspan => local_serializer};
56
57    let reprc = quote! {
58        _savefile::prelude::Packed
59    };
60
61    let mut deferred_reprc: Option<(usize /*align*/, Vec<TokenStream>)> = None;
62    fn realize_any_deferred(
63        local_serializer: &TokenStream,
64        deferred_reprc: &mut Option<(usize, Vec<TokenStream>)>,
65        output: &mut Vec<TokenStream>,
66    ) {
67        let local_serializer: TokenStream = local_serializer.clone();
68        if let Some((_align, deferred)) = deferred_reprc.take() {
69            assert_eq!(deferred.is_empty(), false);
70            let mut conditions = vec![];
71            for item in deferred.windows(2) {
72                let a = item[0].clone();
73                let b = item[1].clone();
74                if conditions.is_empty() == false {
75                    conditions.push(quote!(&&));
76                }
77                conditions.push(quote!(
78                    std::ptr::addr_of!(#a).add(1) as *const u8 == std::ptr::addr_of!(#b) as *const u8
79                ));
80            }
81            if conditions.is_empty() {
82                conditions.push(quote!(true));
83            }
84            let mut fallbacks = vec![];
85            for item in deferred.iter() {
86                fallbacks.push(quote!(
87                <_ as _savefile::prelude::Serialize>::serialize(&#item, #local_serializer)?;
88                ));
89            }
90            if deferred.len() == 1 {
91                return output.push(quote!( #(#fallbacks)* ));
92            }
93            let mut iter = deferred.into_iter();
94            let deferred_from = iter.next().expect("expected deferred_from");
95            let deferred_to = iter.last().unwrap_or(deferred_from.clone());
96
97            output.push(
98                quote!(
99                    unsafe {
100                        if #(#conditions)* {
101                         #local_serializer.raw_write_region(self,&#deferred_from,&#deferred_to, local_serializer.file_version)?;
102                        } else {
103                            #(#fallbacks)*
104                        }
105                    }
106            ));
107        }
108    }
109
110    let get_obj_id = |field: &FieldInfo| -> TokenStream {
111        let objid = if index {
112            assert!(implicit_self);
113            let id = syn::Index {
114                index: field.index,
115                span,
116            };
117            quote! { self.#id}
118        } else {
119            let id = field.ident.clone().expect("Expected identifier[3]");
120            if implicit_self {
121                quote! { self.#id}
122            } else {
123                quote! { *#id}
124            }
125        };
126        objid
127    };
128
129    for field in &field_infos {
130        {
131            let verinfo = parse_attr_tag(field.attrs);
132
133            if verinfo.ignore {
134                continue;
135            }
136            let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
137
138            let removed = check_is_remove(field.ty);
139
140            let type_size_align = compile_time_size(field.ty);
141            let compile_time_reprc = compile_time_check_reprc(field.ty) && type_size_align.is_some();
142
143            let obj_id = get_obj_id(field);
144
145            if field_from_version == 0 && field_to_version == std::u32::MAX {
146                if removed.is_removed() {
147                    abort!(
148                        field.ty.span(),
149                        "The Removed type can only be used for removed fields. Use the savefile_versions attribute."
150                    );
151                }
152
153                if compile_time_reprc {
154                    let (_cursize, curalign) = type_size_align.expect("type_size_align");
155                    if let Some((deferred_align, deferred_items)) = &mut deferred_reprc {
156                        if *deferred_align == curalign {
157                            deferred_items.push(obj_id);
158                            continue;
159                        }
160                    } else {
161                        deferred_reprc = Some((curalign, vec![obj_id]));
162                        continue;
163                    }
164                }
165                realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
166
167                output.push(quote!(
168                <_ as _savefile::prelude::Serialize>::serialize(&#obj_id, #local_serializer)?;
169                ));
170            } else {
171                realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
172
173                output.push(quote!(
174                if #local_serializer.file_version >= #field_from_version && #local_serializer.file_version <= #field_to_version {
175                    <_ as _savefile::prelude::Serialize>::serialize(&#obj_id, #local_serializer)?;
176                }));
177            }
178        }
179    }
180    realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
181
182    //let contents = format!("//{:?}",output);
183
184    let total_reprc_opt: TokenStream;
185    if field_infos.is_empty() == false {
186        let first_field = get_obj_id(field_infos.first().expect("field_infos.first"));
187        let last_field = get_obj_id(field_infos.last().expect("field_infos.last"));
188        total_reprc_opt = quote!( unsafe { #local_serializer.raw_write_region(self,&#first_field, &#last_field, local_serializer.file_version)?; } );
189    } else {
190        total_reprc_opt = quote!();
191    }
192
193    let serialize2 = quote! {
194        let local_serializer = serializer;
195
196        if unsafe { <Self as #reprc>::repr_c_optimization_safe(local_serializer.file_version).is_yes() } {
197            #total_reprc_opt
198        } else {
199            #(#output)*
200        }
201    };
202
203    let fields_names = field_infos
204        .iter()
205        .map(|field| {
206            let fieldname = field.ident.clone();
207            quote! { #fieldname }
208        })
209        .collect();
210    (serialize2, fields_names)
211}
212
213pub(crate) mod common;
214
215mod serialize;
216
217mod deserialize;
218
219mod savefile_abi;
220
221#[proc_macro_error]
222#[proc_macro_attribute]
223pub fn savefile_abi_exportable(
224    attr: proc_macro::TokenStream,
225    input: proc_macro::TokenStream,
226) -> proc_macro::TokenStream {
227    let parsed: ItemTrait = syn::parse(input.clone()).expect("Expected valid rust-code");
228
229    let mut version = None;
230    for item in attr.to_string().split(',') {
231        let keyvals: Vec<_> = item.split('=').collect();
232        if keyvals.len() != 2 {
233            abort!(
234                item.span(),
235                "savefile_abi_exportable arguments should be of form #[savefile_abi_exportable(version=0)], not '{}'",
236                attr
237            );
238        }
239        let key = keyvals[0].trim();
240        let val = keyvals[1].trim();
241        match key {
242            "version" => {
243                if version.is_some() {
244                    abort!(item.span(), "version specified more than once");
245                }
246                version = Some(
247                    val.parse()
248                        .unwrap_or_else(|_| abort!(item.span(), "Version must be numeric, but was: {}", val)),
249                );
250            }
251            _ => abort!(item.span(), "Unknown savefile_abi_exportable key: '{}'", key),
252        }
253    }
254
255    for attr in &parsed.attrs {
256        let name_segs: Vec<_> = attr.path().segments.iter().map(|x| &x.ident).collect();
257        if name_segs == ["async_trait"] || name_segs == ["async_trait", "async_trait"] {
258            abort!(attr.path().segments.span(), "async_trait-attribute macro detected. The {} macro must go _before_ the #[savefile_abi_exportable(..)] macro!",
259            attr.to_token_stream());
260        }
261    }
262    let version: u32 = version.unwrap_or(0);
263
264    let trait_name_str = parsed.ident.to_string();
265    let trait_name = parsed.ident;
266    let defspan = proc_macro2::Span::mixed_site();
267    let uses = quote_spanned! { defspan =>
268        extern crate savefile;
269        extern crate savefile_abi;
270        extern crate savefile_derive;
271        use savefile::prelude::{Packed, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, get_schema, get_result_schema, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,ReceiverType,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition};
272        use savefile_abi::{parse_return_value_impl,abi_result_receiver,abi_boxed_trait_receiver, FlexBuffer, AbiExportable, TraitObject, PackagedTraitObject, Owning, AbiErrorMsg, RawAbiCallResult, AbiConnection, AbiConnectionMethod, AbiProtocol, abi_entry_light, AbiWaker};
273        use std::collections::HashMap;
274        use std::mem::MaybeUninit;
275        use std::io::Cursor;
276        use std::pin::Pin;
277        use std::marker::Unpin;
278        use std::future::Future;
279        use std::task::{Waker, Poll, Context};
280        use std::sync::Arc;
281        use savefile_derive::savefile_abi_exportable;
282    };
283
284    let mut method_metadata: Vec<TokenStream> = vec![];
285    let mut callee_method_trampoline: Vec<TokenStream> = vec![];
286    let mut caller_method_trampoline = vec![];
287    let mut extra_definitions = HashMap::new();
288
289    if parsed.generics.params.is_empty() == false {
290        abort!(
291            parsed.generics.params.span(),
292            "Savefile does not support generic traits."
293        );
294    }
295    let mut send = false;
296    let mut sync = false;
297    for supertrait in parsed.supertraits.iter() {
298        match supertrait {
299            TypeParamBound::Trait(trait_bound) => {
300                if let Some(lif) = &trait_bound.lifetimes {
301                    abort!(lif.span(), "Savefile does not support lifetimes");
302                }
303                if let Some(seg) = trait_bound.path.segments.last() {
304                    let id = seg.ident.to_string();
305                    match id.as_str() {
306                        "Copy" => abort!(seg.span(), "Savefile does not support Copy bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be copy."),
307                        "Clone" => abort!(seg.span(), "Savefile does not support Clone bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be clone."),
308                        /* these are ok, the wrappers actually do implement these*/
309                        "Sync" => { sync = true;}
310                        "Send" => { send = true;}
311                        "Sized" => {}
312                        "Debug" => {}
313                        _ => abort!(seg.span(), "Savefile does not support bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper doesn't know how to implement arbitrary bounds."),
314                    }
315                }
316            }
317            TypeParamBound::Lifetime(lif) => {
318                if lif.ident != "static" {
319                    abort!(lif.span(), "Savefile does not support lifetimes");
320                }
321            }
322            TypeParamBound::PreciseCapture(c) => {
323                abort!(c.span(), "Savefile does not support precise captures");
324            }
325            TypeParamBound::Verbatim(v) => {
326                abort!(v.span(), "Savefile does not support verbatim bounds");
327            }
328            x => {
329                abort!(x.span(), "Savefile does not support this syntax");
330            }
331        }
332    }
333
334    if parsed.generics.where_clause.is_some() {
335        abort!(
336            parsed.generics.where_clause.span(),
337            "Savefile does not support where-clauses for traits"
338        );
339    }
340
341    for (method_number, item) in parsed.items.iter().enumerate() {
342        if method_number > u16::MAX.into() {
343            abort!(item.span(), "Savefile only supports 2^16 methods per interface. Sorry.");
344        }
345        let method_number = method_number as u16;
346
347        match item {
348            TraitItem::Const(c) => {
349                abort!(
350                    c.span(),
351                    "savefile_abi_exportable does not support associated consts: {}",
352                    c.ident
353                );
354            }
355            TraitItem::Fn(method) => {
356                let mut is_ok = true;
357                let mut async_trait_life_time = 0;
358                let mut life0_life_time = 0;
359                if let Some(wher) = &method.sig.generics.where_clause {
360                    for w in wher.predicates.iter() {
361                        match w {
362                            WherePredicate::Type(t) => {
363                                match &t.bounded_ty {
364                                    Type::Path(p) => {
365                                        if p.path.segments.len() == 1 {
366                                            if p.path.segments[0].ident != "Self" {
367                                                is_ok = false;
368                                            }
369                                        } else {
370                                            is_ok = false;
371                                        }
372                                    }
373                                    _ => {
374                                        is_ok = false;
375                                        break;
376                                    }
377                                }
378                                if let Some(l) = &t.lifetimes {
379                                    is_ok = false;
380                                }
381                                for bound in &t.bounds {
382                                    match bound {
383                                        TypeParamBound::Trait(t) => {
384                                            if t.path.segments.len() == 1 {
385                                                if t.path.segments[0].ident != "Sync" {
386                                                    is_ok = false;
387                                                }
388                                            } else {
389                                                is_ok = false;
390                                            }
391                                        }
392                                        TypeParamBound::Lifetime(l) => {
393                                            if l.ident != "async_trait" {
394                                                is_ok = false;
395                                            }
396                                        }
397                                        x => {
398                                            abort!(x.span(), "Savefile does not support this syntax",);
399                                        }
400                                    }
401                                }
402                            }
403                            WherePredicate::Lifetime(l) => {
404                                if l.lifetime.ident != "life0" {
405                                    if !is_life(&l.lifetime) {
406                                        is_ok = false;
407                                    }
408                                } else {
409                                    life0_life_time += 1;
410                                }
411                                for bound in &l.bounds {
412                                    if bound.ident != "async_trait" {
413                                        is_ok = false;
414                                    } else {
415                                        async_trait_life_time += 1;
416                                    }
417                                }
418                            }
419                            x => {
420                                abort!(x.span(), "Savefile does not support this syntax");
421                            }
422                        }
423                    }
424                    if !is_ok {
425                        abort!(
426                            method.sig.generics.where_clause.span(),
427                            "Savefile does not support where-clauses for methods"
428                        );
429                    }
430                }
431
432                let method_name = method.sig.ident.clone();
433
434                let mut current_name_index = 0u32;
435                let name_baseplate = format!("Temp{}_{}", trait_name_str, method_name);
436                let mut temp_name_generator = move || {
437                    current_name_index += 1;
438                    format!("{}_{}", name_baseplate, current_name_index)
439                };
440                let mut receiver_is_mut = false;
441                let mut receiver_is_pin = false;
442                let ret_type: Type;
443                let ret_declaration;
444                let no_return;
445
446                match &method.sig.output {
447                    ReturnType::Default => {
448                        ret_type = Tuple(TypeTuple {
449                            paren_token: Paren::default(),
450                            elems: Default::default(),
451                        });
452                        ret_declaration = quote! {};
453                        no_return = true;
454                    }
455                    ReturnType::Type(_, ty) => {
456                        ret_type = (**ty).clone();
457                        match &**ty {
458                            Type::Tuple(tup) if tup.elems.is_empty() => {
459                                ret_declaration = quote! {};
460                                no_return = true;
461                            }
462                            _ => {
463                                ret_declaration = quote! { -> #ret_type };
464                                no_return = false;
465                            }
466                        }
467                    }
468                }
469
470                let self_arg = method.sig.inputs.iter().next().unwrap_or_else(|| {
471                    abort!(
472                        method.span(),
473                        "Method '{}' has no arguments. This is not supported by savefile-abi - it must at least have a self-argument.",
474                        method_name
475                    )
476                });
477                let unsupported = || {
478                    abort!(
479                                        method.sig.span(),
480                                        "Method '{}' has an unsupported 'self'-parameter. Try '&self', '&mut self', or 'self: Pin<&mut Self>'. Not supported: {}",
481                                        method_name, self_arg.to_token_stream()
482                                    );
483                };
484
485                let mut parse_receiver_ty = |typ: &Type| {
486                    if let Type::Path(path) = typ {
487                        if !is_well_known(&path.path.segments, ["std", "pin", "Pin"]) {
488                            unsupported();
489                        }
490                        let seg = &path.path.segments.last().unwrap();
491                        let PathArguments::AngleBracketed(args) = &seg.arguments else {
492                            unsupported();
493                            unreachable!();
494                        };
495                        if args.args.len() != 1 {
496                            unsupported();
497                        }
498                        let arg = &args.args[0];
499                        let GenericArgument::Type(Type::Reference(typref)) = arg else {
500                            unsupported();
501                            unreachable!();
502                        };
503                        if typref.mutability.is_none() {
504                            abort!(
505                                            method.sig.span(),
506                                            "Method '{}' has an unsupported 'self'-parameter. Non-mutable references in Pin are presently not supported: {}",
507                                            method_name, self_arg.to_token_stream()
508                                        );
509                        }
510                        let Type::Path(typepath) = &*typref.elem else {
511                            unsupported();
512                            unreachable!();
513                        };
514                        if typepath.path.segments.len() != 1 {
515                            unsupported();
516                            unreachable!()
517                        };
518                        if typepath.path.segments[0].ident != "Self" {
519                            unsupported();
520                        }
521                        receiver_is_mut = true;
522                        receiver_is_pin = true;
523                    } else {
524                        unsupported();
525                    }
526                };
527                match self_arg {
528                    FnArg::Receiver(recv) => {
529                        if recv.colon_token.is_some() {
530                            parse_receiver_ty(&*recv.ty);
531                        } else {
532                            if let Some(reference) = &recv.reference {
533                                if let Some(reference) = &reference.1 {
534                                    if reference.ident != "life0" {
535                                        abort!(
536                                        reference.span(),
537                                        "Method '{}' has a lifetime \"'{}\" for 'self' argument. This is not supported by savefile-abi",
538                                        method_name,
539                                        reference.ident,
540                                    );
541                                    } else {
542                                        life0_life_time += 1;
543                                    }
544                                }
545                                if recv.mutability.is_some() {
546                                    receiver_is_mut = true;
547                                }
548                            } else {
549                                abort!(
550                                self_arg.span(),
551                                "Method '{}' takes 'self' by value. This is not supported by savefile-abi. Use &self",
552                                method_name
553                            );
554                            }
555                        }
556                    }
557                    FnArg::Typed(pat) => match &*pat.pat {
558                        Pat::Ident(ident) if ident.ident == "self" => {
559                            if ident.by_ref.is_some() || ident.mutability.is_some() {
560                                unsupported();
561                            }
562                            parse_receiver_ty(&*pat.ty);
563                        }
564                        _ => {
565                            abort!(
566                                        pat.pat.span(),
567                                        "Method '{}' must have 'self'-parameter (savefile-abi does not support methods without self)",
568                                        method_name
569                                    );
570                        }
571                    },
572                }
573                let mut args = Vec::with_capacity(method.sig.inputs.len());
574                for arg in method.sig.inputs.iter().skip(1) {
575                    match arg {
576                        FnArg::Typed(typ) => {
577                            match &*typ.pat {
578                                Pat::Ident(name) => {
579                                    args.push((name.ident.clone(), &*typ.ty));
580                                }
581                                _ => abort!(typ.pat.span(), "Method '{}' has a parameter which contains a complex pattern. This is not supported by savefile-abi.", method_name)
582                            }
583                        },
584                        _ => abort!(arg.span(), "Unexpected error: method {} had a self parameter that wasn't the first parameter!", method_name)
585                    }
586                }
587                if method.sig.asyncness.is_some() {
588                    let out = match &method.sig.output {
589                        ReturnType::Default => {
590                            quote! {()}
591                        }
592                        ReturnType::Type(_, t) => t.to_token_stream(),
593                    };
594                    abort!(
595                        method.sig.asyncness.span(),
596                        "savefile-abi does not support async methods. You can try returning a boxed future instead: Pin<Box<Future<Output={}>>>",
597                        out
598                    )
599                }
600                if method.sig.variadic.is_some() {
601                    abort!(
602                        method.sig.variadic.span(),
603                        "savefile-abi does not support variadic methods."
604                    )
605                }
606                if method.sig.unsafety.is_some() {
607                    abort!(
608                        method.sig.unsafety.span(),
609                        "savefile-abi does not presently support unsafe methods."
610                    )
611                }
612                if method.sig.abi.is_some() {
613                    abort!(method.sig.abi.span(), "savefile-abi does not need (or support) 'extern \"C\"' or similar ABI-constructs. Just remove this keyword.")
614                }
615
616                // NOTE!
617                // This is part of the heuristics that detects async_trait macro output.
618                // However, we don't actually support reference arguments to functions
619                // returning futures.
620                fn is_life(id: &syn::Lifetime) -> bool {
621                    let s = id.ident.to_string();
622                    if !s.starts_with("life") {
623                        return false;
624                    }
625                    s.strip_prefix("life").unwrap().parse::<usize>().is_ok()
626                }
627
628                if method.sig.generics.params.is_empty() == false {
629                    for item in method.sig.generics.params.iter() {
630                        match item {
631                            GenericParam::Type(typ) => {
632                                abort!(typ.span(), "savefile-abi does not support generic methods.")
633                            }
634                            GenericParam::Const(typ) => {
635                                abort!(typ.span(), "savefile-abi does not support const-generic methods.")
636                            }
637                            GenericParam::Lifetime(l) => {
638                                if l.lifetime.ident != "life0"
639                                    && l.lifetime.ident != "async_trait"
640                                    && !is_life(&l.lifetime)
641                                {
642                                    abort!(
643                                        method.sig.generics.params.span(),
644                                        "savefile-abi does not support methods with lifetimes."
645                                    );
646                                } else {
647                                    if l.lifetime.ident == "life0" {
648                                        life0_life_time += 1;
649                                    }
650                                    async_trait_life_time += 1;
651                                }
652                            }
653                        }
654                    }
655                }
656
657                let async_trait_macro_detected;
658                if life0_life_time >= 3 && async_trait_life_time >= 3 {
659                    async_trait_macro_detected = true;
660                } else if life0_life_time == 0 && async_trait_life_time == 0 {
661                    async_trait_macro_detected = false;
662                } else {
663                    abort!(
664                        item.span(),
665                        "savefile-abi has heuristics that detects the use of the #[async_trait]-macro. This heuristic produced a partial result. It is possible that an incompatible version of async_trait crate has been used. Diagnostics: {} {}",
666                        life0_life_time, async_trait_life_time
667                    );
668                }
669
670                let method_defs = crate::savefile_abi::generate_method_definitions(
671                    version,
672                    trait_name.clone(),
673                    method_number,
674                    method_name,
675                    ret_declaration,
676                    ret_type,
677                    no_return,
678                    receiver_is_mut,
679                    receiver_is_pin,
680                    args,
681                    &mut temp_name_generator,
682                    &mut extra_definitions,
683                    async_trait_macro_detected,
684                );
685                method_metadata.push(method_defs.method_metadata);
686                callee_method_trampoline.push(method_defs.callee_method_trampoline);
687                caller_method_trampoline.push(method_defs.caller_method_trampoline);
688            }
689            TraitItem::Type(t) => {
690                abort!(
691                    t.span(),
692                    "savefile_abi_exportable does not support associated types: {}",
693                    t.ident
694                );
695            }
696            TraitItem::Macro(m) => {
697                abort!(
698                    m.span(),
699                    "savefile_abi_exportable does not support macro items: {:?}",
700                    m
701                );
702            }
703            TraitItem::Verbatim(v) => {
704                abort!(
705                    v.span(),
706                    "Unsupported item in trait definition: {}",
707                    v.to_token_stream()
708                );
709            }
710            x => abort!(
711                x.span(),
712                "Unsupported item in trait definition: {}",
713                x.to_token_stream()
714            ),
715        }
716    }
717
718    let abi_entry_light = Ident::new(&format!("abi_entry_light_{}", trait_name_str), Span::call_site());
719
720    let exports_for_trait = quote! {
721
722        unsafe extern "C" fn #abi_entry_light(flag: AbiProtocol) {
723            unsafe { abi_entry_light::<dyn #trait_name>(flag); }
724        }
725
726        #[automatically_derived]
727        unsafe impl AbiExportable for dyn #trait_name {
728            const ABI_ENTRY : unsafe extern "C" fn (flag: AbiProtocol)  = #abi_entry_light;
729            fn get_definition( version: u32) -> AbiTraitDefinition {
730                AbiTraitDefinition {
731                    name: #trait_name_str.to_string(),
732                    methods: vec! [ #(#method_metadata,)* ],
733                    sync: #sync,
734                    send: #send
735                }
736            }
737
738            fn get_latest_version() -> u32 {
739                #version
740            }
741
742            fn call(trait_object: TraitObject, method_number: u16, effective_version:u32, compatibility_mask: u64, data: &[u8], abi_result: *mut (), __savefile_internal_receiver: unsafe extern "C" fn(outcome: *const RawAbiCallResult, result_receiver: *mut ()/*Result<T,SaveFileError>>*/)) -> Result<(),SavefileError> {
743
744                let mut cursor = Cursor::new(data);
745
746                let mut deserializer = Deserializer {
747                    file_version: cursor.read_u32::<LittleEndian>()?,
748                    reader: &mut cursor,
749                    ephemeral_state: HashMap::new(),
750                };
751
752                match method_number {
753                    #(#callee_method_trampoline,)*
754                    _ => {
755                        return Err(SavefileError::general("Unknown method number"));
756                    }
757                }
758                Ok(())
759            }
760        }
761
762        #[automatically_derived]
763        impl #trait_name for AbiConnection<dyn #trait_name> {
764            #(#caller_method_trampoline)*
765        }
766    };
767
768    //let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
769    let input = TokenStream::from(input);
770    let extra_definitions: Vec<_> = extra_definitions.values().map(|(_, x)| x).collect();
771    let expanded = quote! {
772        #[allow(clippy::double_comparisons)]
773        #[allow(clippy::needless_question_mark)]
774        #[allow(unused_variables)]
775        #[allow(clippy::needless_late_init)]
776        #[allow(clippy::not_unsafe_ptr_arg_deref)]
777        #[allow(non_upper_case_globals)]
778        #[allow(clippy::manual_range_contains)]
779        #[allow(non_local_definitions)]
780        const _:() = {
781            #uses
782
783            #(#extra_definitions)*
784
785            #exports_for_trait
786
787        };
788
789        #input
790    };
791
792    // For debugging, uncomment to write expanded procmacro to file
793    // std::fs::write(format!("/home/anders/savefile/savefile-min-build/src/{}.rs",trait_name_str),expanded.to_string()).unwrap();
794
795    expanded.into()
796}
797#[proc_macro_error]
798#[proc_macro]
799pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
800    let tokens = proc_macro2::TokenStream::from(item);
801
802    let mut tokens_iter = tokens.into_iter();
803
804    let Some(implementing_type) = tokens_iter.next() else {
805        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
806    };
807    let Some(comma) = tokens_iter.next() else {
808        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
809    };
810    if let TokenTree::Punct(p) = comma {
811        if p.as_char() != ',' {
812            abort!(p.span(), "Expected a comma (','). The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements, and these must be separated by a comma.");
813        }
814    } else {
815        abort!(comma.span(), "Expected a comma (','). The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements, and these must be separated by a comma.");
816    }
817    let Some(trait_type) = tokens_iter.next() else {
818        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements. Expected trait name.");
819    };
820
821    if let Some(extra) = tokens_iter.next() {
822        abort!(extra.span(), "Unexpected token. The macro savefile_abi_export! requires exactly two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
823    }
824
825    let defspan = Span::call_site();
826    let uses = quote_spanned! { defspan =>
827        extern crate savefile_abi;
828        use savefile_abi::{AbiProtocol, AbiExportableImplementation, abi_entry,parse_return_value_impl};
829    };
830
831    let abi_entry = Ident::new(
832        ("abi_entry_".to_string() + &trait_type.to_string()).as_str(),
833        Span::call_site(),
834    );
835
836    let expanded = quote! {
837        #[allow(clippy::needless_question_mark)]
838        #[allow(clippy::double_comparisons)]
839        #[allow(non_local_definitions)]
840        const _:() = {
841            #uses
842            #[automatically_derived]
843            unsafe impl AbiExportableImplementation for #implementing_type where #implementing_type: Default + #trait_type {
844                const ABI_ENTRY: unsafe extern "C" fn (AbiProtocol) = #abi_entry;
845                type AbiInterface = dyn #trait_type;
846
847                fn new() -> Box<Self::AbiInterface> {
848                    std::boxed::Box::new(#implementing_type::default())
849                }
850            }
851            #[no_mangle]
852            unsafe extern "C" fn #abi_entry(flag: AbiProtocol) where #implementing_type: Default + #trait_type {
853                unsafe { abi_entry::<#implementing_type>(flag); }
854            }
855        };
856    };
857
858    expanded.into()
859}
860
861#[proc_macro_error]
862#[proc_macro_derive(
863    Savefile,
864    attributes(
865        savefile_unsafe_and_fast,
866        savefile_require_fast,
867        savefile_versions,
868        savefile_versions_as,
869        savefile_introspect_ignore,
870        savefile_introspect_key,
871        savefile_ignore,
872        savefile_default_val,
873        savefile_default_fn
874    )
875)]
876pub fn savefile(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
877    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [Savefile]");
878
879    let s = serialize::savefile_derive_crate_serialize(input.clone());
880
881    let d = deserialize::savefile_derive_crate_deserialize(input.clone());
882
883    let w = savefile_derive_crate_withschema(input.clone());
884
885    let i = savefile_derive_crate_introspect(input.clone());
886
887    let r = derive_reprc_new(input);
888
889    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
890
891    let expanded = quote! {
892        #s
893
894        #d
895
896        #i
897
898        #[allow(non_upper_case_globals)]
899        #[allow(clippy::double_comparisons)]
900        #[allow(non_camel_case_types)]
901        #[allow(clippy::manual_range_contains)]
902        const #dummy_const: () = {
903            extern crate savefile as _savefile;
904            use std::mem::MaybeUninit;
905            use savefile::prelude::Packed;
906
907            #w
908            #r
909        };
910
911    };
912    //std::fs::write("/home/anders/savefile/savefile-min-build/src/expanded.rs", expanded.to_string()).unwrap();
913
914    expanded.into()
915}
916#[proc_macro_error]
917#[proc_macro_derive(
918    SavefileNoIntrospect,
919    attributes(
920        savefile_unsafe_and_fast,
921        savefile_require_fast,
922        savefile_versions,
923        savefile_versions_as,
924        savefile_ignore,
925        savefile_introspect_ignore,
926        savefile_default_val,
927        savefile_default_fn
928    )
929)]
930pub fn savefile_no_introspect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
931    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [SavefileNoIntrospect]");
932
933    let s = serialize::savefile_derive_crate_serialize(input.clone());
934
935    let d = deserialize::savefile_derive_crate_deserialize(input.clone());
936
937    let w = savefile_derive_crate_withschema(input.clone());
938
939    let r = derive_reprc_new(input);
940
941    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
942
943    let expanded = quote! {
944        #s
945
946        #d
947
948        #[allow(non_upper_case_globals)]
949        #[allow(clippy::double_comparisons)]
950        #[allow(clippy::manual_range_contains)]
951        const #dummy_const: () = {
952            extern crate savefile as _savefile;
953            use std::mem::MaybeUninit;
954            use savefile::prelude::Packed;
955
956            #w
957            #r
958        };
959    };
960
961    expanded.into()
962}
963
964#[proc_macro_error]
965#[proc_macro_derive(
966    SavefileIntrospectOnly,
967    attributes(
968        savefile_versions,
969        savefile_versions_as,
970        savefile_introspect_ignore,
971        savefile_ignore,
972        savefile_default_val,
973        savefile_default_fn
974    )
975)]
976pub fn savefile_introspect_only(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
977    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [SavefileIntrospectOnly]");
978
979    let i = savefile_derive_crate_introspect(input);
980
981    let expanded = quote! {
982        #i
983    };
984
985    expanded.into()
986}
987
988#[allow(non_snake_case)]
989fn implement_reprc_hardcoded_false(name: syn::Ident, input: &DeriveInput) -> TokenStream {
990    let defspan = proc_macro2::Span::call_site();
991
992    let generics = &input.generics;
993    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
994    let extra_where = get_extra_where_clauses(input, where_clause, quote! {_savefile::prelude::WithSchema});
995    let reprc = quote_spanned! {defspan=>
996        _savefile::prelude::Packed
997    };
998    let isreprc = quote_spanned! {defspan=>
999        _savefile::prelude::IsPacked
1000    };
1001    quote! {
1002
1003        #[automatically_derived]
1004        impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where{
1005            #[allow(unused_comparisons,unused_variables, unused_variables)]
1006            unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1007                #isreprc::no()
1008            }
1009        }
1010
1011    }
1012}
1013
1014#[allow(non_snake_case)]
1015fn implement_reprc_struct(
1016    field_infos: Vec<FieldInfo>,
1017    input: &DeriveInput,
1018    name: syn::Ident,
1019    expect_fast: bool,
1020) -> TokenStream {
1021    let generics = &input.generics;
1022    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1023    let extra_where = get_extra_where_clauses(input, where_clause, quote! {_savefile::prelude::Packed});
1024
1025    let span = proc_macro2::Span::call_site();
1026    let defspan = proc_macro2::Span::call_site();
1027    let reprc = quote_spanned! {defspan=>
1028        _savefile::prelude::Packed
1029    };
1030    let isreprc = quote_spanned! {defspan=>
1031        _savefile::prelude::IsPacked
1032    };
1033    let offsetof = quote_spanned! {defspan=>
1034        _savefile::prelude::offset_of
1035    };
1036    let local_file_version = quote_spanned! { defspan => local_file_version};
1037    //let WithSchema = quote_spanned! { defspan => _savefile::prelude::WithSchema};
1038    let mut min_safe_version = 0;
1039    let mut packed_outputs = Vec::new();
1040    let mut reprc_outputs = Vec::new();
1041
1042    for field in field_infos.windows(2) {
1043        let field_name1 = field[0].get_accessor();
1044        let field_name2 = field[1].get_accessor();
1045        let ty = field[0].ty;
1046        packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #field_name1) + std::mem::size_of::<#ty>() == #offsetof!(#name #ty_generics, #field_name2) )));
1047    }
1048    if field_infos.len() > 0 {
1049        if field_infos.len() == 1 {
1050            let ty = field_infos[0].ty;
1051            let field_name = field_infos[0].get_accessor();
1052            packed_outputs.push(quote!(  (#offsetof!( #name #ty_generics, #field_name) == 0 )));
1053            packed_outputs.push(quote!(  (#offsetof!( #name #ty_generics, #field_name) + std::mem::size_of::<#ty>() == std::mem::size_of::<#name #ty_generics>() )));
1054        } else {
1055            let first = field_infos.first().expect("field_infos.first()[2]").get_accessor();
1056            let last_field = field_infos.last().expect("field_infos.last()[2]");
1057            let last = last_field.get_accessor();
1058            let last_ty = &last_field.ty;
1059            packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #first) == 0 )));
1060            packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #last) + std::mem::size_of::<#last_ty>()  == std::mem::size_of::<#name #ty_generics>() )));
1061        }
1062    }
1063
1064    for field in &field_infos {
1065        let verinfo = parse_attr_tag(field.attrs);
1066        if verinfo.ignore {
1067            if expect_fast {
1068                abort!(
1069                    field.field_span,
1070                    "The #[savefile_require_fast] attribute cannot be used for structures containing ignored fields"
1071                );
1072            } else {
1073                return implement_reprc_hardcoded_false(name, input);
1074            }
1075        }
1076        let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
1077
1078        let removed = check_is_remove(field.ty);
1079        let field_type = &field.ty;
1080        if field_from_version == 0 && field_to_version == std::u32::MAX {
1081            if removed.is_removed() {
1082                if expect_fast {
1083                    abort!(field.ty.span(), "The Removed type can only be used for removed fields. Use the savefile_version attribute to mark a field as only existing in previous versions.");
1084                } else {
1085                    return implement_reprc_hardcoded_false(name, input);
1086                }
1087            }
1088            reprc_outputs
1089                .push(quote_spanned!( span => <#field_type as #reprc>::repr_c_optimization_safe(#local_file_version).is_yes()));
1090        } else {
1091            min_safe_version = min_safe_version.max(verinfo.min_safe_version());
1092
1093            if !removed.is_removed() {
1094                reprc_outputs.push(
1095                    quote_spanned!( span => <#field_type as #reprc>::repr_c_optimization_safe(#local_file_version).is_yes()),
1096                );
1097            }
1098        }
1099    }
1100
1101    let require_packed = if expect_fast {
1102        quote!(
1103            const _: () = {
1104                if !PACKED {
1105                    panic!("Memory layout not optimal - requires padding which disables savefile-optimization");
1106                }
1107            };
1108        )
1109    } else {
1110        quote!()
1111    };
1112
1113    let packed_storage = if generics.params.is_empty() == false {
1114        quote!(let)
1115    } else {
1116        quote!(const)
1117    };
1118
1119    quote! {
1120
1121        #[automatically_derived]
1122        impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where {
1123            #[allow(unused_comparisons,unused_variables, unused_variables)]
1124            unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1125                let local_file_version = file_version;
1126                #packed_storage PACKED : bool = true #( && #packed_outputs)*;
1127                #require_packed
1128                if file_version >= #min_safe_version && PACKED #( && #reprc_outputs)*{
1129                    unsafe { #isreprc::yes() }
1130                } else {
1131                    #isreprc::no()
1132                }
1133            }
1134        }
1135    }
1136}
1137
1138#[derive(Debug)]
1139struct EnumSize {
1140    discriminant_size: u8,
1141    #[allow(unused)] //Keep around, useful for debugging
1142    repr_c: bool,
1143    explicit_size: bool,
1144}
1145
1146fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> EnumSize {
1147    let mut size_u8: Option<u8> = None;
1148    let mut repr_c_seen = false;
1149    let mut have_seen_explicit_size = false;
1150
1151    for attr in attrs.iter() {
1152        let path = attr.path();
1153
1154        if path.is_ident("repr") {
1155            if let Err(err) = attr.parse_nested_meta(|meta| {
1156                //for x in &metalist.nested {
1157                if meta.path.segments.len() != 1 {
1158                    abort!(
1159                        meta.path.span(),
1160                        "Unsupported repr(X) attribute on enum: {}",
1161                        meta.path.to_token_stream()
1162                    );
1163                }
1164                match meta.path.segments[0].ident.to_string().as_str() {
1165                    "C" => repr_c_seen = true,
1166                    "u8" => {
1167                        size_u8 = Some(1);
1168                        have_seen_explicit_size = true;
1169                    }
1170                    "i8" => {
1171                        size_u8 = Some(1);
1172                        have_seen_explicit_size = true;
1173                    }
1174                    "u16" => {
1175                        size_u8 = Some(2);
1176                        have_seen_explicit_size = true;
1177                    }
1178                    "i16" => {
1179                        size_u8 = Some(2);
1180                        have_seen_explicit_size = true;
1181                    }
1182                    "u32" => {
1183                        size_u8 = Some(4);
1184                        have_seen_explicit_size = true;
1185                    }
1186                    "i32" => {
1187                        size_u8 = Some(4);
1188                        have_seen_explicit_size = true;
1189                    }
1190                    "u64" | "i64" => {
1191                        abort!(
1192                            meta.path.span(),
1193                            "Savefile does not support enums with more than 2^32 variants."
1194                        )
1195                    }
1196                    _ => abort!(
1197                        meta.path.span(),
1198                        "Unsupported repr(X) attribute on enum: {}",
1199                        meta.path.to_token_stream()
1200                    ),
1201                };
1202                Ok(())
1203            }) {
1204                abort!(attr.span(), "Unsupported repr(X) attribute: {}", attr.to_token_stream());
1205            };
1206        }
1207    }
1208
1209    let discriminant_size = size_u8.unwrap_or_else(|| {
1210        if actual_variants <= 256 {
1211            1
1212        } else if actual_variants <= 65536 {
1213            2
1214        } else {
1215            if actual_variants >= u32::MAX as usize {
1216                abort_call_site!("The enum had an unreasonable number of variants");
1217            }
1218            4
1219        }
1220    });
1221    EnumSize {
1222        discriminant_size,
1223        repr_c: repr_c_seen,
1224        explicit_size: have_seen_explicit_size,
1225    }
1226}
1227
1228#[proc_macro_error]
1229#[proc_macro_derive(
1230    Packed,
1231    attributes(
1232        savefile_versions,
1233        savefile_versions_as,
1234        savefile_ignore,
1235        savefile_default_val,
1236        savefile_default_fn
1237    )
1238)]
1239pub fn reprc(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1240    abort_call_site!("The #[derive(Packed)] style of unsafe performance opt-in has been removed. The performance gains are now available automatically for any packed struct.")
1241}
1242fn derive_reprc_new(input: DeriveInput) -> TokenStream {
1243    let name = &input.ident;
1244    let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
1245
1246    let mut opt_in_fast = false;
1247    for attr in input.attrs.iter() {
1248        if attr.path().is_ident("savefile_unsafe_and_fast") || attr.path().is_ident("savefile_require_fast") {
1249            opt_in_fast = true;
1250        }
1251        /*match attr.parse_meta() {
1252            Ok(ref meta) => match meta {
1253                &syn::Meta::Path(ref x) => {
1254                    let x = path_to_string(x);
1255                    if x == "savefile_unsafe_and_fast" || x == "savefile_require_fast" {
1256                        opt_in_fast = true;
1257                    }
1258                }
1259                _ => {}
1260            },
1261            _ => {}
1262        }*/
1263    }
1264
1265    /*if !opt_in_fast {
1266        return implement_reprc_hardcoded_false(name, input.generics);
1267    }*/
1268
1269    let expanded = match &input.data {
1270        &syn::Data::Enum(ref enum1) => {
1271            let enum_size = get_enum_size(&input.attrs, enum1.variants.len());
1272            let any_fields = enum1.variants.iter().any(|v| v.fields.len() > 0);
1273            if !enum_size.explicit_size {
1274                if opt_in_fast {
1275                    if any_fields {
1276                        abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8),C],#[repr(u16,C)] or #[repr(u32,C)], attribute.");
1277                    } else {
1278                        abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8)],#[repr(u16)] or #[repr(u32)], attribute.");
1279                    }
1280                }
1281                return implement_reprc_hardcoded_false(name.clone(), &input);
1282            }
1283
1284            let mut conditions = vec![];
1285
1286            let mut min_safe_version: u32 = 0;
1287
1288            let mut unique_field_types = HashSet::new();
1289
1290            let fn_impl_generics = if !input.generics.params.is_empty() {
1291                quote! { :: #impl_generics}
1292            } else {
1293                quote! {}
1294            };
1295            for (variant_index, variant) in enum1.variants.iter().enumerate() {
1296                let mut attrs: Vec<_> = vec![];
1297
1298                let mut num_fields = 0usize;
1299                let mut field_types = vec![];
1300                match &variant.fields {
1301                    &syn::Fields::Named(ref fields_named) => {
1302                        for field in fields_named.named.iter() {
1303                            attrs.push(&field.attrs);
1304                            field_types.push(&field.ty);
1305                            num_fields += 1;
1306                        }
1307                    }
1308                    &syn::Fields::Unnamed(ref fields_unnamed) => {
1309                        for field in fields_unnamed.unnamed.iter() {
1310                            attrs.push(&field.attrs);
1311                            field_types.push(&field.ty);
1312                            num_fields += 1;
1313                        }
1314                    }
1315                    &syn::Fields::Unit => {}
1316                }
1317                for i in 0usize..num_fields {
1318                    let verinfo = parse_attr_tag(&attrs[i]);
1319                    if check_is_remove(&field_types[i]).is_removed() {
1320                        if verinfo.version_to == u32::MAX {
1321                            abort!(field_types[i].span(), "Removed fields must have a max version, provide one using #[savefile_versions=\"..N\"]")
1322                        }
1323                        min_safe_version = min_safe_version.max(verinfo.version_to + 1);
1324                    }
1325                    let typ = field_types[i].to_token_stream();
1326
1327                    let variant_index = proc_macro2::Literal::u32_unsuffixed(variant_index as u32);
1328
1329                    unique_field_types.insert(field_types[i].clone());
1330                    if i == 0 {
1331                        let discriminant_bytes = enum_size.discriminant_size as usize;
1332                        conditions.push( quote!( (#discriminant_bytes == (get_variant_offsets #fn_impl_generics(#variant_index)[#i])) ) );
1333                    }
1334                    if i == num_fields - 1 {
1335                        conditions.push(
1336                            quote!(  (std::mem::size_of::<#name #ty_generics>() == (get_variant_offsets #fn_impl_generics(#variant_index)[#i]) + std::mem::size_of::<#typ>())  )
1337                        );
1338                    } else {
1339                        let n = i + 1;
1340                        let end_offset_condition = quote!(  (get_variant_offsets #fn_impl_generics(#variant_index)[#n] == (get_variant_offsets #fn_impl_generics(#variant_index)[#i]) + std::mem::size_of::<#typ>())  );
1341                        conditions.push(quote!(#end_offset_condition));
1342                    };
1343                }
1344
1345                for attr in attrs {
1346                    let verinfo = parse_attr_tag(attr);
1347                    if verinfo.ignore {
1348                        if opt_in_fast {
1349                            abort_call_site!(
1350                                "The #[savefile_require_fast] attribute cannot be used for structures containing ignored fields"
1351                            );
1352                        } else {
1353                            return implement_reprc_hardcoded_false(name.clone(), &input);
1354                        }
1355                    }
1356                    min_safe_version = min_safe_version.max(verinfo.min_safe_version());
1357                }
1358            }
1359
1360            let defspan = proc_macro2::Span::call_site();
1361            let generics = &input.generics;
1362            let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1363            let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::Packed});
1364            let reprc = quote_spanned! { defspan=>
1365                _savefile::prelude::Packed
1366            };
1367            let isreprc = quote_spanned! {defspan=>
1368                _savefile::prelude::IsPacked
1369            };
1370
1371            if conditions.is_empty() {
1372                conditions.push(quote!(true));
1373            }
1374            let require_packed = if opt_in_fast {
1375                quote!(
1376                    const _: () = {
1377                        if !PACKED {
1378                            panic!("Memory layout not optimal - requires padding which disables savefile-optimization");
1379                        }
1380                    };
1381                )
1382            } else {
1383                quote!()
1384            };
1385            let mut reprc_condition = vec![];
1386            for typ in unique_field_types {
1387                reprc_condition.push(quote!(
1388                    <#typ as Packed>::repr_c_optimization_safe(file_version).is_yes()
1389                ));
1390            }
1391
1392            let packed_decl = if generics.params.is_empty() {
1393                quote! { const }
1394            } else {
1395                quote! { let }
1396            };
1397            let packed_constraints = if any_fields {
1398                quote!(
1399                    #packed_decl PACKED : bool = true #( && #conditions)*;
1400                    #require_packed
1401                    if !PACKED {
1402                        return #isreprc::no();
1403                    }
1404                )
1405            } else {
1406                quote!()
1407            };
1408
1409            return quote! {
1410
1411                #[automatically_derived]
1412                impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where {
1413                    #[allow(unused_comparisons,unused_variables, unused_variables)]
1414                    unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1415                        let local_file_version = file_version;
1416
1417                        #packed_constraints
1418
1419                        if file_version >= #min_safe_version #( && #reprc_condition)* {
1420                            unsafe { #isreprc::yes() }
1421                        } else {
1422                            #isreprc::no()
1423                        }
1424                    }
1425                }
1426            };
1427
1428            //implement_reprc_struct(vec![], input.generics, name, opt_in_fast) //Hacky, consider enum without any fields as a field-less struct
1429        }
1430        &syn::Data::Struct(ref struc) => match &struc.fields {
1431            &syn::Fields::Named(ref namedfields) => {
1432                let field_infos: Vec<FieldInfo> = namedfields
1433                    .named
1434                    .iter()
1435                    .enumerate()
1436                    .map(|(field_index, field)| FieldInfo {
1437                        ident: Some(field.ident.clone().expect("Expected identifier [8]")),
1438                        field_span: field.ident.as_ref().unwrap().span(),
1439                        index: field_index as u32,
1440                        ty: &field.ty,
1441                        attrs: &field.attrs,
1442                    })
1443                    .collect();
1444
1445                implement_reprc_struct(field_infos, &input, name.clone(), opt_in_fast)
1446            }
1447            &syn::Fields::Unnamed(ref fields_unnamed) => {
1448                let field_infos: Vec<FieldInfo> = fields_unnamed
1449                    .unnamed
1450                    .iter()
1451                    .enumerate()
1452                    .map(|(idx, field)| FieldInfo {
1453                        field_span: field.ty.span(),
1454                        ident: None,
1455                        index: idx as u32,
1456                        ty: &field.ty,
1457                        attrs: &field.attrs,
1458                    })
1459                    .collect();
1460
1461                implement_reprc_struct(field_infos, &input, name.clone(), opt_in_fast)
1462            }
1463            &syn::Fields::Unit => implement_reprc_struct(Vec::new(), &input, name.clone(), opt_in_fast),
1464        },
1465        _ => {
1466            if opt_in_fast {
1467                abort_call_site!("Unsupported data type");
1468            }
1469            return implement_reprc_hardcoded_false(name.clone(), &input);
1470        }
1471    };
1472
1473    expanded
1474}
1475
1476#[allow(non_snake_case)]
1477fn implement_introspect(
1478    field_infos: Vec<FieldInfo>,
1479    need_self: bool,
1480) -> (Vec<TokenStream>, Vec<TokenStream>, Option<TokenStream>) {
1481    let span = proc_macro2::Span::call_site();
1482    let defspan = proc_macro2::Span::call_site();
1483
1484    //let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1485    //let Introspect = quote_spanned! { defspan => _savefile::prelude::Introspect };
1486    //let fields1=quote_spanned! { defspan => fields1 };
1487    let index1 = quote_spanned! { defspan => index };
1488    let introspect_item = quote_spanned! { defspan=>
1489        _savefile::prelude::introspect_item
1490    };
1491
1492    let mut fields = Vec::new();
1493    let mut fields_names = Vec::new();
1494    let mut introspect_key = None;
1495    let mut index_number = 0usize;
1496    for (idx, field) in field_infos.iter().enumerate() {
1497        let verinfo = parse_attr_tag(field.attrs);
1498        if verinfo.introspect_key && introspect_key.is_some() {
1499            abort!(
1500                field.field_span,
1501                "Type had more than one field with savefile_introspect_key - attribute"
1502            );
1503        }
1504        if verinfo.introspect_ignore {
1505            continue;
1506        }
1507        if need_self {
1508            let fieldname;
1509            let fieldname_raw;
1510
1511            let id = field.get_accessor();
1512            fieldname = quote! {&self.#id};
1513            fieldname_raw = quote! {#id};
1514
1515            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(stringify!(#fieldname_raw).to_string(), #fieldname))}));
1516            if verinfo.introspect_key {
1517                let fieldname_raw2 = fieldname_raw.clone();
1518                introspect_key = Some(quote! {self.#fieldname_raw2});
1519            }
1520            fields_names.push(fieldname_raw);
1521        } else if let Some(id) = field.ident.clone() {
1522            let fieldname;
1523            let quoted_fieldname;
1524            let raw_fieldname = id.to_string();
1525            let id2 = id.clone();
1526            fieldname = id;
1527            quoted_fieldname = quote! { #fieldname };
1528            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(#raw_fieldname.to_string(), #quoted_fieldname))}));
1529            fields_names.push(quoted_fieldname);
1530            if verinfo.introspect_key {
1531                introspect_key = Some(quote!(#id2))
1532            }
1533        } else {
1534            let fieldname;
1535            let quoted_fieldname;
1536            let raw_fieldname = idx.to_string();
1537            fieldname = Ident::new(&format!("v{}", idx), span);
1538            let fieldname2 = fieldname.clone();
1539            quoted_fieldname = quote! { #fieldname };
1540            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(#raw_fieldname.to_string(), #quoted_fieldname))}));
1541            fields_names.push(quoted_fieldname);
1542            if verinfo.introspect_key {
1543                introspect_key = Some(quote!(#fieldname2))
1544            }
1545        }
1546
1547        index_number += 1;
1548    }
1549
1550    (fields_names, fields, introspect_key)
1551}
1552
1553#[allow(non_snake_case)]
1554fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream {
1555    let name = &input.ident;
1556
1557    let generics = &input.generics;
1558    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1559    let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::Introspect});
1560
1561    let span = proc_macro2::Span::call_site();
1562    let defspan = proc_macro2::Span::call_site();
1563    let introspect = quote_spanned! {defspan=>
1564        _savefile::prelude::Introspect
1565    };
1566    let introspect_item_type = quote_spanned! {defspan=>
1567        _savefile::prelude::IntrospectItem
1568    };
1569    let uses = quote_spanned! { defspan =>
1570        extern crate savefile as _savefile;
1571    };
1572
1573    //let SchemaStruct = quote_spanned! { defspan => _savefile::prelude::SchemaStruct };
1574    //let SchemaEnum = quote_spanned! { defspan => _savefile::prelude::SchemaEnum };
1575    //let Schema = quote_spanned! { defspan => _savefile::prelude::Schema };
1576    //let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1577    //let Variant = quote_spanned! { defspan => _savefile::prelude::Variant };
1578
1579    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
1580
1581    let expanded = match &input.data {
1582        &syn::Data::Enum(ref enum1) => {
1583            let mut variants = Vec::new();
1584            let mut value_variants = Vec::new();
1585            let mut len_variants = Vec::new();
1586            for variant in enum1.variants.iter() {
1587                /*if var_idx >= 256 {
1588                    panic!("Savefile does not support enums with 256 variants or more. Sorry.");
1589                }*/
1590                //let var_idx = var_idx as u8;
1591                let var_ident = variant.ident.clone();
1592                let variant_name = quote! { #var_ident };
1593                let variant_name_spanned = quote_spanned! { span => #variant_name};
1594
1595                let mut field_infos = Vec::new();
1596
1597                let return_value_name_str = format!("{}::{}", name, var_ident);
1598                let return_value_name = quote!(#return_value_name_str);
1599                match &variant.fields {
1600                    &syn::Fields::Named(ref fields_named) => {
1601                        for (idx, f) in fields_named.named.iter().enumerate() {
1602                            field_infos.push(FieldInfo {
1603                                ident: Some(f.ident.clone().expect("Expected identifier[9]")),
1604                                field_span: f.ident.as_ref().unwrap().span(),
1605                                index: idx as u32,
1606                                ty: &f.ty,
1607                                attrs: &f.attrs,
1608                            });
1609                        }
1610                        let (fields_names, fields, introspect_key) = implement_introspect(field_infos, false);
1611                        let fields_names1 = fields_names.clone();
1612                        let fields_names2 = fields_names.clone();
1613                        let fields_names3 = fields_names.clone();
1614                        let num_fields = fields_names3.len();
1615                        if let Some(introspect_key) = introspect_key {
1616                            value_variants.push(quote!(#name::#variant_name_spanned{#(#fields_names,)*} => {
1617                                #introspect_key.to_string()
1618                            }
1619                            ));
1620                        } else {
1621                            value_variants.push(quote!( #name::#variant_name_spanned{#(#fields_names2,)*} => {
1622                                #return_value_name.to_string()
1623                            } ));
1624                        }
1625                        variants.push(quote!( #name::#variant_name_spanned{#(#fields_names1,)*} => {
1626                                #(#fields;)*
1627                            } ));
1628                        len_variants.push(quote!( #name::#variant_name_spanned{#(#fields_names3,)*} => {
1629                                #num_fields
1630                            } ));
1631                    }
1632                    &syn::Fields::Unnamed(ref fields_unnamed) => {
1633                        for (idx, f) in fields_unnamed.unnamed.iter().enumerate() {
1634                            field_infos.push(FieldInfo {
1635                                field_span: f.ty.span(),
1636                                ident: None,
1637                                index: idx as u32,
1638                                ty: &f.ty,
1639                                attrs: &f.attrs,
1640                            });
1641                        }
1642                        let (fields_names, fields, introspect_key) = implement_introspect(field_infos, false);
1643                        let fields_names1 = fields_names.clone();
1644                        let fields_names2 = fields_names.clone();
1645                        let fields_names3 = fields_names.clone();
1646                        let num_fields = fields_names3.len();
1647
1648                        if let Some(introspect_key) = introspect_key {
1649                            value_variants.push(quote!( #name::#variant_name_spanned(#(#fields_names1,)*) => {
1650                                    #introspect_key.to_string()
1651                            }));
1652                        } else {
1653                            value_variants.push(
1654                                quote!( #name::#variant_name_spanned(#(#fields_names2,)*) => #return_value_name.to_string() )
1655                            );
1656                        }
1657
1658                        variants.push(quote!( #name::#variant_name_spanned(#(#fields_names,)*) => { #(#fields;)* } ));
1659                        len_variants.push(quote!( #name::#variant_name_spanned(#(#fields_names3,)*) => {
1660                                #num_fields
1661                            } ));
1662                    }
1663                    &syn::Fields::Unit => {
1664                        //No fields
1665                        variants.push(quote! {
1666                            #name::#variant_name_spanned => {}
1667                        });
1668                        value_variants.push(quote!( #name::#variant_name_spanned => #return_value_name.to_string() ));
1669                        len_variants.push(quote!( #name::#variant_name_spanned => 0));
1670                    }
1671                }
1672
1673                //variants.push(quote!{})
1674            }
1675            quote! {
1676                #[allow(non_upper_case_globals)]
1677                #[allow(clippy::double_comparisons)]
1678                #[allow(clippy::manual_range_contains)]
1679                const #dummy_const: () = {
1680                    #uses
1681
1682                    #[automatically_derived]
1683                    impl #impl_generics #introspect for #name #ty_generics #where_clause #extra_where {
1684
1685                        #[allow(unused_mut)]
1686                        #[allow(unused_comparisons, unused_variables)]
1687                        fn introspect_value(&self) -> String {
1688                            match self {
1689                                #(#value_variants,)*
1690                            }
1691                        }
1692                        #[allow(unused_mut)]
1693                        #[allow(unused_comparisons, unused_variables)]
1694                        fn introspect_child(&self, index:usize) -> Option<Box<dyn #introspect_item_type+'_>> {
1695                            match self {
1696                                #(#variants,)*
1697                            }
1698                            return None;
1699                        }
1700                        #[allow(unused_mut)]
1701                        #[allow(unused_comparisons, unused_variables)]
1702                        fn introspect_len(&self) -> usize {
1703                            match self {
1704                                #(#len_variants,)*
1705                            }
1706                        }
1707
1708                    }
1709                };
1710            }
1711        }
1712        &syn::Data::Struct(ref struc) => {
1713            let fields;
1714            match &struc.fields {
1715                &syn::Fields::Named(ref namedfields) => {
1716                    let field_infos: Vec<FieldInfo> = namedfields
1717                        .named
1718                        .iter()
1719                        .enumerate()
1720                        .map(|(idx, field)| FieldInfo {
1721                            ident: Some(field.ident.clone().expect("Expected identifier[10]")),
1722                            field_span: field.ident.as_ref().unwrap().span(),
1723                            ty: &field.ty,
1724                            index: idx as u32,
1725                            attrs: &field.attrs,
1726                        })
1727                        .collect();
1728
1729                    fields = implement_introspect(field_infos, true);
1730                }
1731                &syn::Fields::Unnamed(ref fields_unnamed) => {
1732                    let field_infos: Vec<FieldInfo> = fields_unnamed
1733                        .unnamed
1734                        .iter()
1735                        .enumerate()
1736                        .map(|(idx, f)| FieldInfo {
1737                            ident: None,
1738                            field_span: f.ty.span(),
1739                            ty: &f.ty,
1740                            index: idx as u32,
1741                            attrs: &f.attrs,
1742                        })
1743                        .collect();
1744
1745                    fields = implement_introspect(field_infos, true);
1746                }
1747                &syn::Fields::Unit => {
1748                    fields = (Vec::new(), Vec::new(), None);
1749                }
1750            }
1751            let fields1 = fields.1;
1752            let introspect_key: Option<TokenStream> = fields.2;
1753            let field_count = fields1.len();
1754            let value_name;
1755            if let Some(introspect_key) = introspect_key {
1756                value_name = quote! { #introspect_key.to_string()};
1757            } else {
1758                value_name = quote! { stringify!(#name).to_string() };
1759            }
1760            quote! {
1761                #[allow(non_upper_case_globals)]
1762                #[allow(clippy::double_comparisons)]
1763                #[allow(clippy::manual_range_contains)]
1764                const #dummy_const: () = {
1765                    #uses
1766
1767                    #[automatically_derived]
1768                    impl #impl_generics #introspect for #name #ty_generics #where_clause #extra_where {
1769                        #[allow(unused_comparisons)]
1770                        #[allow(unused_mut, unused_variables)]
1771                        fn introspect_value(&self) -> String {
1772                            #value_name
1773                        }
1774                        #[allow(unused_comparisons)]
1775                        #[allow(unused_mut, unused_variables)]
1776                        fn introspect_child(&self, index: usize) -> Option<Box<dyn #introspect_item_type+'_>> {
1777                            #(#fields1;)*
1778                            return None;
1779                        }
1780                        fn introspect_len(&self) -> usize {
1781                            #field_count
1782                        }
1783                    }
1784                };
1785            }
1786        }
1787        _ => {
1788            abort_call_site!("Unsupported datatype");
1789        }
1790    };
1791
1792    expanded
1793}
1794
1795#[allow(non_snake_case)]
1796fn implement_withschema(
1797    structname: &str,
1798    field_infos: Vec<FieldInfo>,
1799    is_enum: FieldOffsetStrategy,
1800    generics: &Generics,
1801    ty_generics: &TypeGenerics,
1802    impl_generics: &ImplGenerics,
1803) -> Vec<TokenStream> {
1804    let span = proc_macro2::Span::call_site();
1805    let defspan = proc_macro2::Span::call_site();
1806    let local_version = quote_spanned! { defspan => local_version};
1807    let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1808    let WithSchema = quote_spanned! { defspan => _savefile::prelude::WithSchema };
1809    let fields1 = quote_spanned! { defspan => fields1 };
1810
1811    let structname = Ident::new(structname, defspan);
1812    let offset_of = quote_spanned! {defspan=>
1813        _savefile::prelude::offset_of
1814    };
1815
1816    let fn_impl_generics = if !generics.params.is_empty() {
1817        quote! { :: #impl_generics}
1818    } else {
1819        quote! {}
1820    };
1821    let mut fields = Vec::new();
1822    for (idx, field) in field_infos.iter().enumerate() {
1823        let verinfo = parse_attr_tag(field.attrs);
1824        if verinfo.ignore {
1825            continue;
1826        }
1827        let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
1828
1829        let offset;
1830        match is_enum {
1831            FieldOffsetStrategy::EnumWithKnownOffsets(variant_index) => {
1832                offset = quote! { Some(get_variant_offsets #fn_impl_generics (#variant_index)[#idx]) };
1833            }
1834            FieldOffsetStrategy::EnumWithUnknownOffsets => {
1835                offset = quote! { None };
1836            }
1837            FieldOffsetStrategy::Struct => {
1838                if let Some(name) = field.ident.clone() {
1839                    offset = quote! { Some(#offset_of!(#structname #ty_generics, #name)) };
1840                } else {
1841                    let idx = Index::from(idx);
1842                    offset = quote! { Some(#offset_of!(#structname #ty_generics, #idx)) }
1843                };
1844            }
1845        }
1846
1847        let name_str = if let Some(name) = field.ident.clone() {
1848            name.to_string()
1849        } else {
1850            idx.to_string()
1851        };
1852        let removed = check_is_remove(field.ty);
1853        let field_type = &field.ty;
1854        if field_from_version == 0 && field_to_version == u32::MAX {
1855            if removed.is_removed() {
1856                abort!(
1857                    field.ty.span(),
1858                    "The Removed type can only be used for removed fields. Use the savefile_version attribute."
1859                );
1860            }
1861            fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset)} )));
1862        } else {
1863            let mut version_mappings = Vec::new();
1864            let offset = if field_to_version != u32::MAX {
1865                quote!(None)
1866            } else {
1867                offset
1868            };
1869            for dt in verinfo.deserialize_types.iter() {
1870                let dt_from = dt.from;
1871                let dt_to = dt.to;
1872                let dt_field_type = syn::Ident::new(&dt.serialized_type, span);
1873                // We don't supply offset in this case, deserialized type doesn't match field type
1874                version_mappings.push(quote!{
1875                    if #local_version >= #dt_from && local_version <= #dt_to {
1876                        #fields1.push(#Field ::new( #name_str.to_string(), std::boxed::Box::new(<#dt_field_type as #WithSchema>::schema(#local_version, context))) );
1877                    }
1878                });
1879            }
1880
1881            fields.push(quote_spanned!( span =>
1882                #(#version_mappings)*
1883
1884                if #local_version >= #field_from_version && #local_version <= #field_to_version {
1885                    #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset )} );
1886                }
1887                ));
1888        }
1889    }
1890    fields
1891}
1892
1893enum FieldOffsetStrategy {
1894    Struct,
1895    EnumWithKnownOffsets(usize /*variant index*/),
1896    EnumWithUnknownOffsets,
1897}
1898
1899fn get_phantomdata_types_from_type(ty: &Type, mut in_phantom: bool, generic_params_to_constraint: &mut HashSet<Ident>) {
1900    match ty {
1901        Type::Array(a) => {
1902            get_phantomdata_types_from_type(&*a.elem, in_phantom, generic_params_to_constraint);
1903        }
1904        Type::BareFn(f) => {
1905            match &f.output {
1906                ReturnType::Default => {}
1907                ReturnType::Type(_, ty) => {
1908                    get_phantomdata_types_from_type(&*ty, in_phantom, generic_params_to_constraint);
1909                }
1910            }
1911            for arg in &f.inputs {
1912                get_phantomdata_types_from_type(&arg.ty, in_phantom, generic_params_to_constraint);
1913            }
1914        }
1915        Type::Paren(ty) => {
1916            get_phantomdata_types_from_type(&*ty.elem, in_phantom, generic_params_to_constraint);
1917        }
1918        Type::Path(pd) => {
1919            if pd.path.segments.len() == 1 {
1920                let id = &pd.path.segments.last().unwrap().ident;
1921                if id == "PhantomData" {
1922                    in_phantom = true;
1923                }
1924                if !in_phantom {
1925                    generic_params_to_constraint.insert(id.clone());
1926                }
1927            }
1928            for param in &pd.path.segments {
1929                match &param.arguments {
1930                    PathArguments::None => {}
1931                    PathArguments::AngleBracketed(a) => {
1932                        for arg in &a.args {
1933                            match arg {
1934                                GenericArgument::Type(t) => {
1935                                    get_phantomdata_types_from_type(&*t, in_phantom, generic_params_to_constraint);
1936                                }
1937                                GenericArgument::AssocType(a) => {
1938                                    get_phantomdata_types_from_type(&a.ty, in_phantom, generic_params_to_constraint);
1939                                }
1940                                _ => {}
1941                            }
1942                        }
1943                    }
1944                    PathArguments::Parenthesized(a) => {
1945                        for arg in &a.inputs {
1946                            get_phantomdata_types_from_type(arg, in_phantom, generic_params_to_constraint);
1947                        }
1948                        match &a.output {
1949                            ReturnType::Default => {}
1950                            ReturnType::Type(_, ty) => {
1951                                get_phantomdata_types_from_type(&*ty, in_phantom, generic_params_to_constraint);
1952                            }
1953                        }
1954                    }
1955                }
1956            }
1957        }
1958        Type::Ptr(_) => {}
1959        Type::Reference(r) => {
1960            get_phantomdata_types_from_type(&*r.elem, in_phantom, generic_params_to_constraint);
1961        }
1962        Type::Slice(s) => {
1963            get_phantomdata_types_from_type(&*s.elem, in_phantom, generic_params_to_constraint);
1964        }
1965        Type::TraitObject(to) => {}
1966        Tuple(typ) => {
1967            for t in &typ.elems {
1968                get_phantomdata_types_from_type(t, in_phantom, generic_params_to_constraint);
1969            }
1970        }
1971        _ => {}
1972    }
1973}
1974fn get_phantomdata_types(data: &syn::Data, in_phantom: bool, generic_params_to_constrain: &mut HashSet<Ident>) {
1975    match data {
1976        Data::Struct(s) => {
1977            for field in s.fields.iter() {
1978                get_phantomdata_types_from_type(&field.ty, in_phantom, generic_params_to_constrain);
1979            }
1980        }
1981        Data::Enum(v) => {
1982            for variant in v.variants.iter() {
1983                for field in variant.fields.iter() {
1984                    get_phantomdata_types_from_type(&field.ty, in_phantom, generic_params_to_constrain);
1985                }
1986            }
1987        }
1988        Data::Union(_) => {}
1989    }
1990}
1991
1992#[allow(non_snake_case)]
1993fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream {
1994    //let mut have_u8 = false;
1995
1996    //let discriminant_size = discriminant_size.expect("Enum discriminant must be u8, u16 or u32. Use for example #[repr(u8)].");
1997
1998    let name = &input.ident;
1999
2000    let generics = &input.generics;
2001
2002    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
2003
2004    let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::WithSchema});
2005
2006    let span = proc_macro2::Span::call_site();
2007    let defspan = proc_macro2::Span::call_site();
2008    let withschema = quote_spanned! {defspan=>
2009        _savefile::prelude::WithSchema
2010    };
2011
2012    let SchemaStruct = quote_spanned! { defspan => _savefile::prelude::SchemaStruct };
2013    let SchemaEnum = quote_spanned! { defspan => _savefile::prelude::SchemaEnum };
2014    let Schema = quote_spanned! { defspan => _savefile::prelude::Schema };
2015    let Field = quote_spanned! { defspan => _savefile::prelude::Field };
2016    let Variant = quote_spanned! { defspan => _savefile::prelude::Variant };
2017
2018    //let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
2019
2020    let expanded = match &input.data {
2021        &syn::Data::Enum(ref enum1) => {
2022            let max_variant_fields = enum1.variants.iter().map(|x| x.fields.len()).max().unwrap_or(0);
2023
2024            let enum_size = get_enum_size(&input.attrs, enum1.variants.len());
2025            let need_determine_offsets = enum_size.explicit_size;
2026
2027            let mut variants = Vec::new();
2028            let mut variant_field_offset_extractors = vec![];
2029            for (var_idx, variant) in enum1.variants.iter().enumerate() {
2030                /*if var_idx >= 256 {
2031                    panic!("Savefile does not support enums with 256 total variants. Sorry.");
2032                }*/
2033                let var_idx = var_idx as u8;
2034                let var_ident = variant.ident.clone();
2035                let variant_name = quote! { #var_ident };
2036                let variant_name_spanned = quote_spanned! { span => stringify!(#variant_name).to_string()};
2037
2038                let verinfo = parse_attr_tag(&variant.attrs);
2039                let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
2040
2041                if field_to_version != std::u32::MAX {
2042                    abort!(
2043                        variant.span(),
2044                        "Savefile automatic derive does not support removal of enum values."
2045                    );
2046                }
2047
2048                let mut field_infos = Vec::new();
2049
2050                let mut field_offset_extractors = vec![];
2051
2052                let offset_extractor_match_clause;
2053                match &variant.fields {
2054                    &syn::Fields::Named(ref fields_named) => {
2055                        let mut field_pattern = vec![];
2056                        for (idx, f) in fields_named.named.iter().enumerate() {
2057                            let field_name = f
2058                                .ident
2059                                .as_ref()
2060                                .expect("Enum variant with named fields *must* actually have a name")
2061                                .clone();
2062                            field_offset_extractors.push(quote!(unsafe { (#field_name as *const _ as *const u8).offset_from(base_ptr) as usize }));
2063                            field_pattern.push(field_name);
2064                            field_infos.push(FieldInfo {
2065                                ident: Some(f.ident.clone().expect("Expected identifier[1]")),
2066                                field_span: f.ident.as_ref().unwrap().span(),
2067                                ty: &f.ty,
2068                                index: idx as u32,
2069                                attrs: &f.attrs,
2070                            });
2071                        }
2072                        offset_extractor_match_clause = quote! {#name::#var_ident { #(#field_pattern,)* } };
2073                    }
2074                    &syn::Fields::Unnamed(ref fields_unnamed) => {
2075                        let mut field_pattern = vec![];
2076                        for (idx, f) in fields_unnamed.unnamed.iter().enumerate() {
2077                            let field_binding = Ident::new(&format!("x{}", idx), Span::call_site());
2078                            field_pattern.push(field_binding.clone());
2079                            field_offset_extractors.push(quote!(unsafe { (#field_binding as *const _ as *const u8).offset_from(base_ptr) as usize }));
2080                            field_infos.push(FieldInfo {
2081                                ident: None,
2082                                field_span: f.ty.span(),
2083                                index: idx as u32,
2084                                ty: &f.ty,
2085                                attrs: &f.attrs,
2086                            });
2087                        }
2088                        offset_extractor_match_clause = quote! {#name::#var_ident ( #(#field_pattern,)* ) };
2089                    }
2090                    &syn::Fields::Unit => {
2091                        offset_extractor_match_clause = quote! {#name::#var_ident};
2092                        //No fields
2093                    }
2094                }
2095                while field_offset_extractors.len() < max_variant_fields {
2096                    field_offset_extractors.push(quote! {0});
2097                }
2098
2099                variant_field_offset_extractors.push(quote! {
2100                   #offset_extractor_match_clause => {
2101                       [ #(#field_offset_extractors,)* ]
2102                   }
2103                });
2104
2105                let field_offset_strategy = if need_determine_offsets && field_infos.is_empty() == false {
2106                    FieldOffsetStrategy::EnumWithKnownOffsets(var_idx as usize)
2107                } else {
2108                    FieldOffsetStrategy::EnumWithUnknownOffsets
2109                };
2110
2111                let fields = implement_withschema(
2112                    &name.to_string(),
2113                    field_infos,
2114                    field_offset_strategy,
2115                    &generics,
2116                    &ty_generics,
2117                    &impl_generics,
2118                );
2119
2120                variants.push(quote! {
2121                (#field_from_version,
2122                 #field_to_version,
2123                 #Variant { name: #variant_name_spanned, discriminant: #var_idx, fields:
2124                    {
2125                        let mut fields1 = Vec::<#Field>::new();
2126                        #(#fields;)*
2127                        fields1
2128                    }}
2129                )});
2130            }
2131
2132            let field_offset_impl;
2133            if need_determine_offsets {
2134                let varbuf_assign;
2135                if enum_size.discriminant_size == 1 {
2136                    varbuf_assign = quote!( varbuf[0] = variant as u8; );
2137                } else if enum_size.discriminant_size == 2 {
2138                    // We only support little endian
2139                    varbuf_assign = quote!(
2140                        varbuf[0] = variant as u8;
2141                        varbuf[1] = (variant>>8) as u8;
2142                    );
2143                } else if enum_size.discriminant_size == 4 {
2144                    // We only support little endian
2145                    varbuf_assign = quote!(
2146                        varbuf[0] = variant as u8;
2147                        varbuf[1] = (variant>>8) as u8;
2148                        varbuf[2] = (variant>>16) as u8;
2149                        varbuf[3] = (variant>>24) as u8;
2150                    );
2151                } else {
2152                    abort_call_site!("Unsupported enum size: {}", enum_size.discriminant_size);
2153                }
2154                let not_const_if_gen = if generics.params.is_empty() {
2155                    quote! {const}
2156                } else {
2157                    quote! {}
2158                };
2159                let conjure_variant;
2160                if generics.params.is_empty() {
2161                    conjure_variant = quote! {
2162                        let mut varbuf = [0u8;std::mem::size_of::<#name #ty_generics>()];
2163                        #varbuf_assign
2164                        let mut value : MaybeUninit<#name #ty_generics> = unsafe { std::mem::transmute(varbuf) };
2165                    }
2166                } else {
2167                    let discr_type;
2168                    match enum_size.discriminant_size {
2169                        1 => discr_type = quote! { u8 },
2170                        2 => discr_type = quote! { u16 },
2171                        4 => discr_type = quote! { u32 },
2172                        _ => unreachable!(),
2173                    }
2174                    conjure_variant = quote! {
2175                        let mut value = MaybeUninit::< #name #ty_generics >::uninit();
2176                        let discr: *mut #discr_type = &mut value as *mut MaybeUninit<#name #ty_generics> as *mut #discr_type;
2177                        unsafe {
2178                            *discr = variant as #discr_type;
2179                        }
2180                    }
2181                }
2182
2183                field_offset_impl = quote! {
2184                    #not_const_if_gen fn get_field_offset_impl #impl_generics (value: &#name #ty_generics) -> [usize;#max_variant_fields] {
2185                        assert!(std::mem::size_of::<#name #ty_generics>()>0);
2186                        let base_ptr = value as *const #name #ty_generics as *const u8;
2187                        match value {
2188                            #(#variant_field_offset_extractors)*
2189                        }
2190                    }
2191                    #not_const_if_gen fn get_variant_offsets #impl_generics(variant: usize) -> [usize;#max_variant_fields] {
2192                        #conjure_variant
2193                        //let base_ptr = &mut value as *mut MaybeUninit<#name> as *mut u8;
2194                        //unsafe { *base_ptr = variant as u8; }
2195                        get_field_offset_impl(unsafe { &*(&value as *const MaybeUninit<#name #ty_generics> as *const #name #ty_generics) } )
2196                    }
2197                };
2198            } else {
2199                field_offset_impl = quote! {};
2200            }
2201
2202            let discriminant_size = enum_size.discriminant_size;
2203            let has_explicit_repr = enum_size.repr_c;
2204
2205            quote! {
2206                #field_offset_impl
2207                #[automatically_derived]
2208                impl #impl_generics #withschema for #name #ty_generics #where_clause #extra_where {
2209                    #[allow(unused_mut)]
2210                    #[allow(unused_comparisons, unused_variables)]
2211                    fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema {
2212                        let local_version = version;
2213
2214                        #Schema::Enum (
2215                            unsafe{#SchemaEnum::new_unsafe(
2216                                stringify!(#name).to_string(),
2217                                (vec![#(#variants),*]).into_iter().filter_map(|(fromver,tover,x)|{
2218                                    if local_version >= fromver && local_version <= tover {
2219                                        Some(x)
2220                                    } else {
2221                                        None
2222                                    }
2223                                }).collect(),
2224                                #discriminant_size,
2225                                #has_explicit_repr,
2226                                Some(std::mem::size_of::<#name #ty_generics>()),
2227                                Some(std::mem::align_of::<#name #ty_generics>()),
2228                            )}
2229                        )
2230                    }
2231                }
2232
2233            }
2234        }
2235        &syn::Data::Struct(ref struc) => {
2236            let fields;
2237            match &struc.fields {
2238                &syn::Fields::Named(ref namedfields) => {
2239                    let field_infos: Vec<FieldInfo> = namedfields
2240                        .named
2241                        .iter()
2242                        .enumerate()
2243                        .map(|(idx, field)| FieldInfo {
2244                            ident: Some(field.ident.clone().expect("Expected identifier[2]")),
2245                            field_span: field.ident.span(),
2246                            ty: &field.ty,
2247                            index: idx as u32,
2248                            attrs: &field.attrs,
2249                        })
2250                        .collect();
2251
2252                    fields = implement_withschema(
2253                        &name.to_string(),
2254                        field_infos,
2255                        FieldOffsetStrategy::Struct,
2256                        &generics,
2257                        &ty_generics,
2258                        &impl_generics,
2259                    );
2260                }
2261                &syn::Fields::Unnamed(ref fields_unnamed) => {
2262                    let field_infos: Vec<FieldInfo> = fields_unnamed
2263                        .unnamed
2264                        .iter()
2265                        .enumerate()
2266                        .map(|(idx, f)| FieldInfo {
2267                            field_span: f.ty.span(),
2268                            ident: None,
2269                            index: idx as u32,
2270                            ty: &f.ty,
2271                            attrs: &f.attrs,
2272                        })
2273                        .collect();
2274                    fields = implement_withschema(
2275                        &name.to_string(),
2276                        field_infos,
2277                        FieldOffsetStrategy::Struct,
2278                        &generics,
2279                        &ty_generics,
2280                        &impl_generics,
2281                    );
2282                }
2283                &syn::Fields::Unit => {
2284                    fields = Vec::new();
2285                }
2286            }
2287            quote! {
2288                #[automatically_derived]
2289                impl #impl_generics #withschema for #name #ty_generics #where_clause #extra_where {
2290
2291                    #[allow(unused_comparisons)]
2292                    #[allow(unused_mut, unused_variables)]
2293                    fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema {
2294                        let local_version = version;
2295                        let mut fields1 = Vec::new();
2296                        #(#fields;)* ;
2297                        #Schema::Struct(unsafe{#SchemaStruct::new_unsafe(
2298                            stringify!(#name).to_string(),
2299                            fields1,
2300                            Some(std::mem::size_of::<#name #ty_generics>()),
2301                            Some(std::mem::align_of::<#name #ty_generics>()),
2302                        )})
2303
2304                    }
2305                }
2306            }
2307        }
2308        _ => {
2309            abort_call_site!("Unsupported datatype");
2310        }
2311    };
2312    // For debugging, uncomment to write expanded procmacro to file
2313    //std::fs::write(format!("/home/anders/savefile/savefile-abi-min-lib/src/expanded.rs"),expanded.to_string()).unwrap();
2314
2315    expanded
2316}