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