Skip to main content

struct_patch/
traits.rs

1/// A struct that a patch can be applied to
2///
3/// Deriving [`Patch`] will generate a patch struct and an accompanying trait impl so that it can be applied to the original struct.
4/// ```rust
5/// # use struct_patch::Patch;
6/// #[derive(Patch)]
7/// struct Item {
8///     field_bool: bool,
9///     field_int: usize,
10///     field_string: String,
11/// }
12///
13/// // Generated struct
14/// // struct ItemPatch {
15/// //     field_bool: Option<bool>,
16/// //     field_int: Option<usize>,
17/// //     field_string: Option<String>,
18/// // }
19/// ```
20/// ## Container attributes
21/// ### `#[patch(attribute(derive(...)))]`
22/// Use this attribute to derive traits on the generated patch struct
23/// ```rust
24/// # use struct_patch::Patch;
25/// # use serde::{Serialize, Deserialize};
26/// #[derive(Patch)]
27/// #[patch(attribute(derive(Debug, Default, Deserialize, Serialize)))]
28/// struct Item;
29///
30/// // Generated struct
31/// // #[derive(Debug, Default, Deserialize, Serialize)]
32/// // struct ItemPatch {}
33/// ```
34///
35/// ### `#[patch(attribute(...))]`
36/// Use this attribute to pass the attributes on the generated patch struct
37/// ```compile_fail
38/// // This example need `serde` and `serde_with` crates
39/// # use struct_patch::Patch;
40/// #[derive(Patch, Debug)]
41/// #[patch(attribute(derive(Serialize, Deserialize, Default)))]
42/// #[patch(attribute(skip_serializing_none))]
43/// struct Item;
44///
45/// // Generated struct
46/// // #[derive(Default, Deserialize, Serialize)]
47/// // #[skip_serializing_none]
48/// // struct ItemPatch {}
49/// ```
50///
51/// ### `#[patch(name = "...")]`
52/// Use this attribute to change the name of the generated patch struct
53/// ```rust
54/// # use struct_patch::Patch;
55/// #[derive(Patch)]
56/// #[patch(name = "ItemOverlay")]
57/// struct Item { }
58///
59/// // Generated struct
60/// // struct ItemOverlay {}
61/// ```
62///
63/// ### `#[patch(default_log(fn_path))]`
64/// Automatically call `fn_path(&str)` with each patched field name inside
65/// every generated `apply` call. Has no effect on `apply_with_log`. The path
66/// may be any function path visible at the call site.
67/// ```rust
68/// # use struct_patch::Patch;
69/// fn log_field(field: &str) { let _ = field; }
70///
71/// #[derive(Default, Patch)]
72/// #[patch(default_log(log_field))]
73/// struct Item {
74///     field_int: usize,
75///     field_string: String,
76/// }
77///
78/// let mut item = Item::default();
79/// item.apply(ItemPatch { field_int: Some(1), field_string: None });
80/// // log_field("field_int") is called automatically
81/// ```
82///
83/// ## Field attributes
84/// ### `#[patch(skip)]`
85/// If you want certain fields to be unpatchable, you can let the derive macro skip certain fields when creating the patch struct
86/// ```rust
87/// # use struct_patch::Patch;
88/// #[derive(Patch)]
89/// struct Item {
90///     #[patch(skip)]
91///     id: String,
92///     data: String,
93/// }
94///
95/// // Generated struct
96/// // struct ItemPatch {
97/// //     data: Option<String>,
98/// // }
99/// ```
100///
101/// ### `#[patch(skip_wrap)]`
102/// Keep the field type as-is in the generated patch struct (no extra `Option`
103/// wrapping). This is useful for fields that are already `Option<...>`,
104/// typically `Option<Vec<_>>`, where the default double-`Option` in the patch
105/// is unwanted. With `skip_wrap`, `None` in the patch means "no change" and
106/// `Some(v)` sets the field to `Some(v)` (including `Some(vec![])` to clear
107/// the vector). Cannot be combined with `empty_value`.
108/// ```rust
109/// # use struct_patch::Patch;
110/// #[derive(Default, Patch)]
111/// struct Item {
112///     #[patch(skip_wrap)]
113///     tags: Option<Vec<String>>,
114/// }
115///
116/// // Generated struct
117/// // struct ItemPatch {
118/// //     tags: Option<Vec<String>>, // not wrapped again
119/// // }
120///
121/// let mut item = Item { tags: Some(vec!["a".into()]) };
122///
123/// // `None` in the patch keeps the field unchanged.
124/// item.apply(ItemPatch { tags: None });
125/// assert_eq!(item.tags, Some(vec!["a".into()]));
126///
127/// // `Some(vec![])` still applies and clears the list.
128/// item.apply(ItemPatch { tags: Some(vec![]) });
129/// assert_eq!(item.tags, Some(vec![]));
130/// ```
131pub trait Patch<P> {
132    /// Apply a patch
133    fn apply(&mut self, patch: P);
134
135    /// Apply a patch, calling `log` with each patched field name.
136    ///
137    /// The default implementation ignores `log` and delegates to [`apply`](Patch::apply).
138    /// The derive macro generates an override that calls `log` once per field that is
139    /// actually changed.
140    ///
141    /// ```rust
142    /// # use struct_patch::Patch;
143    /// #[derive(Default, Patch)]
144    /// struct Item {
145    ///     field_int: usize,
146    ///     field_string: String,
147    /// }
148    ///
149    /// let mut item = Item::default();
150    /// let patch = ItemPatch { field_int: Some(42), field_string: None };
151    ///
152    /// let mut patched_fields = Vec::new();
153    /// item.apply_with_log(patch, |field| patched_fields.push(field.to_string()));
154    ///
155    /// assert_eq!(patched_fields, vec!["field_int"]);
156    /// ```
157    fn apply_with_log<F: FnMut(&str)>(&mut self, patch: P, _log: F) {
158        self.apply(patch);
159    }
160
161    /// Returns a patch that when applied turns any struct of the same type into `Self`
162    fn into_patch(self) -> P;
163
164    /// Returns a patch that when applied turns `previous_struct` into `Self`
165    fn into_patch_by_diff(self, previous_struct: Self) -> P;
166
167    /// Get an empty patch instance
168    fn new_empty_patch() -> P;
169}
170
171pub trait Filler<F> {
172    /// Apply a filler
173    fn apply(&mut self, filler: F);
174
175    /// Apply a filler, calling `log` with each field name that is actually filled.
176    ///
177    /// The default implementation ignores `log` and delegates to [`apply`](Filler::apply).
178    /// The derive macro generates an override that calls `log` once per field that is
179    /// actually filled (i.e. the field was empty and the filler supplied a value).
180    ///
181    /// ```rust
182    /// # use struct_patch::Filler;
183    /// #[derive(Default, Filler)]
184    /// struct Item {
185    ///     value: Option<usize>,
186    /// }
187    ///
188    /// let mut item = Item::default();
189    /// let filler = ItemFiller { value: Some(42) };
190    ///
191    /// let mut filled_fields = Vec::new();
192    /// item.apply_with_log(filler, |field| filled_fields.push(field.to_string()));
193    ///
194    /// assert_eq!(filled_fields, vec!["value"]);
195    /// ```
196    fn apply_with_log<L: FnMut(&str)>(&mut self, filler: F, _log: L) {
197        self.apply(filler);
198    }
199
200    /// Get an empty filler instance
201    fn new_empty_filler() -> F;
202}
203
204#[cfg(feature = "status")]
205/// A patch struct with extra status information
206pub trait Status {
207    /// Returns `true` if all fields are `None`, `false` otherwise.
208    fn is_empty(&self) -> bool;
209}
210
211#[cfg(feature = "merge")]
212/// A patch struct that can be merged to another one
213pub trait Merge {
214    fn merge(self, other: Self) -> Self;
215}
216
217#[cfg(feature = "catalyst")]
218/// A substrate struct that can expose the fields information thereof
219pub trait Substrate {
220    fn expose_content() -> &'static str;
221
222    /// Expose the field information, by call this function in Build.rs
223    fn expose();
224}
225
226#[cfg(feature = "catalyst")]
227/// A catalyst struct that can expose the fields information thereof
228pub trait Catalyst<S, Cpx> {
229    /// catalyst bind on substrate and generate complex
230    fn bind(self, substrate: S) -> Cpx;
231}
232
233#[cfg(feature = "catalyst")]
234/// A complex struct that can decouple return catalyst and substrate
235pub trait Complex<Cat, S> {
236    /// complex decouple to catalyst and substrate
237    fn decouple(self) -> (Cat, S);
238}