revision_derive/lib.rs
1//! Exports the `Revisioned` procedural macro attribute, and the derive procedural
2//! macro that automatically generates the Revisioned trait on structs and enums.
3//!
4//! The `Revisioned` trait is automatically implemented for the following primitives:
5//! u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, char,
6//! String, Vec<T>, Arrays up to 32 elements, Option<T>, Box<T>, Bound<T>, Wrapping<T>,
7//! (A, B), (A, B, C), (A, B, C, D), (A, B, C, D, E), Duration, HashMap<K, V>,
8//! BTreeMap<K, V>, Result<T, E>, Cow<'_, T>, Decimal, regex::Regex, uuid::Uuid,
9//! chrono::Duration, chrono::DateTime<Utc>, geo::Point, geo::LineString geo::Polygon,
10//! geo::MultiPoint, geo::MultiLineString, and geo::MultiPolygon.
11
12use proc_macro::TokenStream;
13
14mod ast;
15mod expand;
16
17/// Generates serialization and deserialization code as an implementation of
18/// the `Revisioned` trait for structs and enums.
19///
20/// This procedural macro attribute currently analyses the struct field and
21/// enum variant revisions, and generates custom serializer and deserializer
22/// implementations for each version. In the future, this procedural macro
23/// will also automatically remove old struct fields entirely, reducing the
24/// memory size of the struct, and ensuring that field types can be changed.
25///
26/// This macro works by generating a single serializer implementation for the
27/// latest revision of a struct, and multiple deserializer implementations for
28/// each historical revision of a struct. There is no limit to the maximum
29/// number of revisions that are possible to be defined for a struct or enum.
30///
31/// ## Revisioned requirements
32///
33/// Currently, all struct field values, and all enum variant fields need to
34/// implement the `Revisioned` trait. This is already implemented for a number
35/// of primitive and custom types. In addition, the `Revisioned` derive macro
36/// can not be used with generics.
37///
38/// ## Attribute annotations
39///
40/// To facilitate version tolerant serialization "history metadata" is attached
41/// to the structure or enum. This is done by using the `revision` attribute for
42/// each field. In the below example a new field is added to the structure
43/// starting with version 2: `#[revision(start = 2)]`. The struct revision must
44/// match the maximum computed revision of every struct field or enum variant.
45///
46/// ```ignore
47/// use revision::revisioned;
48///
49/// #[derive(Debug)]
50/// #[revisioned(revision = 2)]
51/// struct Test {
52/// a: u32,
53/// #[revision(start = 2)]
54/// b: u8,
55/// }
56/// ```
57///
58/// Multiple version annotations can be defined for a field, like for example:
59/// `#[revision(start = 2, end = 3)]`. Field was added in structure version 2
60/// and removed in version 3. The generated code will ensure that this field
61/// will only be deserialized for version 2 of the structure.
62///
63/// ## Disabling serialization or deserialization
64///
65/// You can disable serialization or deserialization of a struct by using the
66/// `#[revision(serialize = false)]` or `#[revision(deserialize = false)]`.
67/// This is useful for data migrations when you no longer want to write the
68/// struct but still want to read it or vice versa.
69///
70/// By default both serialization and deserialization are enabled.
71///
72/// ## Example
73///
74/// ```
75/// use revision::prelude::*;
76///
77/// #[derive(Debug)]
78/// #[revisioned(revision = 2, serialize = false)]
79/// struct ReadOnlyStruct {
80/// a: u32,
81/// }
82///
83/// #[derive(Debug)]
84/// #[revisioned(revision = 2, deserialize = false)]
85/// struct WriteOnlyStruct {
86/// a: u32,
87/// }
88/// ```
89///
90/// ## Supported field attributes and usage
91///
92/// The struct field and enum variant `revision` attribute accepts several key-
93/// value pairs to be specified in order to support struct revisions, default
94/// values for newly added fields, and value conversion for old fields which
95/// have been removed. The macro will automatically detect whether a conversion
96/// function is required for a removed field or variant.
97///
98/// ### start/end
99///
100/// Defines the field revision lifetime. Fields can be added by specifing the
101/// `start` revision number of the structure when first defining them and can
102/// be removed from serialization logic by adding an `end` revision number.
103///
104/// For example: `#[revision(start = 2, end = 4)]`. The field would be present
105/// in the structure at revisions 2 and 3, but starting with revision 4 it would
106/// no longer be serialized or deserialized.
107///
108/// ### default_fn
109///
110/// Provides an initialization value for a field when deserializing from an
111/// older structure version which does not contain this field. If not specified
112/// the `Default` trait is used to initialize the field.
113///
114/// The function name needs to be specified as a string. The first function
115/// argument is the source revision that is being deserialized, and the return
116/// value is the same type as the field or an error.
117///
118/// ```ignore
119/// use revision::Error;
120/// use revision::revisioned;
121///
122/// #[derive(Debug)]
123/// #[revisioned(revision = 2)]
124/// struct TestStruct {
125/// a: u32,
126/// #[version(start = 2, default_fn = "default_b")]
127/// b: u8,
128/// }
129///
130/// impl TestStruct {
131/// fn default_b(_revision: u16) -> Result<u8, Error> {
132/// 12u8
133/// }
134/// }
135/// ```
136///
137/// ### convert_fn
138///
139/// If defined, the method is called when the field existed at some previous
140/// revision, but no longer exists in the latest revision. The implementation
141/// and behaviour is slightly different depending on whether it is applied to
142/// a removed struct field or a removed enum variant or a removed field from an
143/// enum variant. If defined, the function name needs to be specified as a
144/// string, and will be called when the field existed at a previous revision,
145/// but no longer exists in the latest revision.
146///
147/// When defined on a removed struct field, the first function argument is the
148/// `&mut self` of the struct to update, the second argument is the source
149/// revision that was deserialized, and the third argument is the deserialized
150/// value from the field which has been removed.
151///
152/// When working with an enum variant the convert function works with a fields
153/// struct. This is a generated structure which has the same fields as the enum
154/// variant. By default this struct is named
155/// '`{enum name}{variant name}Fields`', this name can be changed with the
156/// `fields_name` if desired.
157///
158/// When a field in a variant is removed the convert
159/// function takes a mutable reference to this fields struct as its first
160/// argument, it's second argument is the revision from which this field is
161/// being deserialized and it's third argument is the deserialized value.
162///
163/// When the entire variant is remove the first argument is the fields
164/// struct with it's fields containing the values of the deserialized removed
165/// variant. In both situations the convert_fn function takes as a second
166/// argument the revision from which this was serialized. The function should
167/// return a result with either the right deserialized value or an error.
168///
169/// ```ignore
170/// use revision::Error;
171/// use revision::revisioned;
172///
173/// #[derive(Debug)]
174/// #[revisioned(revision = 2)]
175/// struct SomeStruct {
176/// some_u32: u32,
177/// #[version(end = 2, convert_fn = "convert_some_u16")]
178/// some_u16: u16,
179/// #[revision(start = 2)]
180/// some_u64: u64,
181/// }
182///
183/// impl SomeStruct {
184/// fn convert_some_u16(&mut self, _revision: u16, value: u16) -> Result<(), Error> {
185/// self.some_u64 = self.some_u16 as u64;
186/// Ok(())
187/// }
188/// }
189///
190/// #[derive(Debug)]
191/// #[revisioned(revision = 3)]
192/// enum SomeTuple {
193/// One,
194/// #[revision(end = 2, convert_fn = "convert_variant_two")]
195/// Two(i64, u32),
196/// #[revision(start = 2)]
197/// Three(i64, u64, #[revision(end = 3, convert_fn = "convert_variant_three_field")] bool),
198/// }
199///
200/// impl SomeTuple {
201/// fn convert_variant_two(fields: SomeTupleTwoFields, _revision: u16) -> Result<Self, Error> {
202/// Ok(Self::Three(fields.a, fields.b as u64, true))
203/// }
204///
205/// fn convert_variant_three_field(fields: &mut SomeTupleTwoFields, _revision: u16, v: bool) -> Result<(), Error> {
206/// Ok(())
207/// }
208/// }
209/// ```
210#[proc_macro_attribute]
211pub fn revisioned(attrs: TokenStream, input: TokenStream) -> proc_macro::TokenStream {
212 match expand::revision(attrs.into(), input.into()) {
213 Ok(x) => x.into(),
214 Err(e) => e.into_compile_error().into(),
215 }
216}