recordkeeper_data_model/
lib.rs

1//! Data model exported by the procedural macros for `SaveBin`.
2
3use std::{any::TypeId, marker::PhantomData};
4
5/// Types that have a well-formed data model for the save file format.
6pub trait Model: 'static {
7    fn model() -> TypeModel;
8}
9
10pub type ModelName = &'static str;
11
12/// The model for a struct type, i.e. how it and its fields are represented
13/// in the save file.
14#[derive(Debug)]
15pub struct StructModel {
16    /// The struct's name.
17    ///
18    /// This does not include generic lifetimes and type parameters.
19    pub name: ModelName,
20    /// A unique identifier for the struct's type, which also considers
21    /// generic parameters.
22    pub type_id: TypeId,
23    pub total_len: usize,
24    pub fields: Vec<FieldModel>,
25}
26
27pub struct StructModelBuilder(StructModel);
28
29/// The model for a struct field.
30#[derive(Debug)]
31pub struct FieldModel {
32    pub name: ModelName,
33    pub type_model: TypeModel,
34    pub offset: usize,
35    pub size: usize,
36}
37
38#[derive(Debug)]
39pub enum TypeModel {
40    /// Empty types
41    Empty,
42    /// Struct types
43    Struct(Box<StructModel>),
44    /// Array types
45    Array(Box<(TypeModel, usize)>),
46    /// Rust primitive types
47    Primitive(ModelName),
48}
49
50impl StructModel {
51    /// Creates a model for a struct with no fields (or all hidden fields).
52    ///
53    /// It is "opaque" because from the user's perspective, it is just a block of bytes with a
54    /// name and size.
55    pub fn new_opaque(name: ModelName, type_id: TypeId, size: usize) -> Self {
56        Self {
57            name,
58            type_id,
59            total_len: size,
60            fields: Vec::new(),
61        }
62    }
63
64    /// Creates a struct model builder.
65    ///
66    /// The builder allows fields to be added to the model.
67    pub fn new_builder(name: ModelName, type_id: TypeId) -> StructModelBuilder {
68        StructModelBuilder(StructModel {
69            name,
70            type_id,
71            total_len: 0,
72            fields: Vec::new(),
73        })
74    }
75}
76
77impl StructModelBuilder {
78    pub fn add_field(
79        &mut self,
80        name: ModelName,
81        type_model: TypeModel,
82        offset: usize,
83        size: usize,
84    ) -> &mut Self {
85        if offset < self.0.total_len {
86            self.0.total_len = offset;
87        }
88        self.0.fields.push(FieldModel {
89            name,
90            offset,
91            type_model,
92            size,
93        });
94        self.0.total_len += size;
95        self
96    }
97
98    pub fn set_total_len(&mut self, size: usize) -> &mut Self {
99        assert!(size >= self.0.total_len);
100        self.0.total_len = size;
101        self
102    }
103
104    pub fn build(self) -> StructModel {
105        self.0
106    }
107}
108
109macro_rules! builtin_impl {
110    ($($types:tt ) *) => {
111        $(
112            impl Model for $types {
113                fn model() -> TypeModel {
114                    TypeModel::Primitive(stringify!($types))
115                }
116            }
117        )*
118    };
119}
120
121builtin_impl!(bool u8 i8 u64 i64 f64 u32 i32 f32 u16 i16);
122
123impl<T, const N: usize> Model for [T; N]
124where
125    T: Model,
126{
127    fn model() -> TypeModel {
128        TypeModel::Array(Box::new((T::model(), N)))
129    }
130}
131
132impl<T, const N: usize> Model for Box<[T; N]>
133where
134    T: Model,
135{
136    fn model() -> TypeModel {
137        TypeModel::Array(Box::new((T::model(), N)))
138    }
139}
140
141impl<T: 'static> Model for PhantomData<T> {
142    fn model() -> TypeModel {
143        TypeModel::Empty
144    }
145}