thisversion/traits.rs
1//! Traits for converting between versioned schemas and application models.
2//!
3//! A schema type represents data in a persisted format; an application model
4//! is the representation the program uses. Implement [`IntoSchema`] to choose
5//! a schema representation when writing data, and implement [`IntoModel`] or
6//! [`TryIntoModel`] to read schema data into the application model.
7//!
8//! These traits consume their input so conversions can move owned fields
9//! without cloning. Container implementations for `Option` (and, with the
10//! `alloc` feature, `Vec`) apply the same conversion to each contained value.
11//! The derive macros can generate the schema and version-family conversions
12//! for common cases.
13
14//============================================================================//
15// INTO SCHEMA //
16//============================================================================//
17
18/// Converts an application model into a selected schema representation.
19///
20/// The result is a data value in the requested representation. It may be a bare
21/// schema value, a version-family value, or a container of schema values. A
22/// model may support multiple target representations.
23///
24/// The `VersionBoundary` derive constructs the latest schema value, then uses
25/// `From` to convert it into `S`. Requesting its version family selects the
26/// latest variant; requesting an older schema requires an explicit conversion
27/// from the latest schema. Such conversions may discard information, so a round
28/// trip through a schema need not preserve the original model.
29pub trait IntoSchema<S>: Sized {
30 /// Converts this model into the requested schema representation.
31 fn into_schema(self) -> S;
32}
33
34impl<T, S> IntoSchema<Option<S>> for Option<T>
35where
36 T: IntoSchema<S>,
37{
38 fn into_schema(self) -> Option<S> {
39 self.map(IntoSchema::into_schema)
40 }
41}
42
43#[cfg(feature = "alloc")]
44impl<T, S> IntoSchema<alloc::vec::Vec<S>> for alloc::vec::Vec<T>
45where
46 T: IntoSchema<S>,
47{
48 fn into_schema(self) -> alloc::vec::Vec<S> {
49 self.into_iter().map(IntoSchema::into_schema).collect()
50 }
51}
52
53//============================================================================//
54// INTO MODEL //
55//============================================================================//
56
57/// Converts a schema value into its application model without a recoverable
58/// conversion error.
59///
60/// Implement this when every value of the schema type can be represented by the
61/// model. Use [`TryIntoModel`] when conversion can reject a value.
62pub trait IntoModel: Sized {
63 /// The application model produced from this schema type.
64 type Model;
65
66 /// Converts this schema value into its application model.
67 fn into_model(self) -> Self::Model;
68}
69
70impl<T: IntoModel> IntoModel for Option<T> {
71 type Model = Option<T::Model>;
72
73 fn into_model(self) -> Self::Model {
74 self.map(IntoModel::into_model)
75 }
76}
77
78#[cfg(feature = "alloc")]
79impl<T: IntoModel> IntoModel for alloc::vec::Vec<T> {
80 type Model = alloc::vec::Vec<T::Model>;
81
82 fn into_model(self) -> Self::Model {
83 self.into_iter().map(IntoModel::into_model).collect()
84 }
85}
86
87//============================================================================//
88// TRY INTO MODEL //
89//============================================================================//
90
91/// Converts a schema, version family, or container into an application model,
92/// allowing conversion to fail.
93///
94/// Fallible latest schemas choose the final error type. Fallible version-family
95/// derives use that same type while migrating and converting the latest schema.
96/// Recursive fields propagate their errors in the same way.
97///
98/// Deriving `VersionBoundary` without an error type also supplies
99/// `TryIntoModel` with [`core::convert::Infallible`] as its error, so both
100/// model conversion entry points remain available to adapters.
101pub trait TryIntoModel: Sized {
102 /// The application model produced from this schema type.
103 type Model;
104
105 /// The error returned when a schema value cannot be represented by the
106 /// application model.
107 type Error;
108
109 /// Converts this schema value into its application model.
110 fn try_into_model(self) -> Result<Self::Model, Self::Error>;
111}
112
113impl<T: TryIntoModel> TryIntoModel for Option<T> {
114 type Model = Option<T::Model>;
115 type Error = T::Error;
116
117 fn try_into_model(self) -> Result<Self::Model, Self::Error> {
118 self.map(TryIntoModel::try_into_model).transpose()
119 }
120}
121
122#[cfg(feature = "alloc")]
123impl<T: TryIntoModel> TryIntoModel for alloc::vec::Vec<T> {
124 type Model = alloc::vec::Vec<T::Model>;
125 type Error = T::Error;
126
127 fn try_into_model(self) -> Result<Self::Model, Self::Error> {
128 self.into_iter().map(TryIntoModel::try_into_model).collect()
129 }
130}