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` with patched field information 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///
68/// When the `nesting` feature is enabled, `fn_path` receives both a prefix path and field name.
69/// When `nesting` is disabled, `fn_path` receives only the field name.
70///
71/// ```rust
72/// # use struct_patch::Patch;
73/// #[derive(Default, Patch)]
74/// struct Item {
75/// field_int: usize,
76/// field_string: String,
77/// }
78///
79/// let mut item = Item::default();
80/// let patch = ItemPatch { field_int: Some(1), field_string: None };
81///
82/// #[cfg(feature = "nesting")]
83/// {
84/// #[cfg(not(feature = "simple-nesting"))]
85/// fn log_field(prefixes: &[&str], field: &str) {
86/// let path = if prefixes.is_empty() {
87/// field.to_string()
88/// } else {
89/// format!("{}.{}", prefixes.join("."), field)
90/// };
91/// println!("patched: {path}");
92/// }
93/// #[cfg(feature = "simple-nesting")]
94/// fn log_field(prefix: &str, field: &str) {
95/// let path = if prefix.is_empty() {
96/// field.to_string()
97/// } else {
98/// format!("{}.{}", prefix, field)
99/// };
100/// println!("patched: {path}");
101/// }
102///
103/// #[derive(Default, Patch)]
104/// #[patch(default_log(log_field))]
105/// struct Config {
106/// field_int: usize,
107/// field_string: String,
108/// }
109/// let mut config = Config::default();
110/// config.apply(ConfigPatch { field_int: Some(1), field_string: None });
111/// // log_field(&[], "field_int") is called automatically without simple-nesting
112/// // log_field("", "field_int") is called automatically with simple-nesting
113/// }
114///
115/// #[cfg(all(feature = "log", not(feature = "nesting")))]
116/// {
117/// fn log_field(field: &str) {
118/// println!("patched: {field}");
119/// }
120///
121/// #[derive(Default, Patch)]
122/// #[patch(default_log(log_field))]
123/// struct Config {
124/// field_int: usize,
125/// field_string: String,
126/// }
127/// let mut config = Config::default();
128/// config.apply(ConfigPatch { field_int: Some(1), field_string: None });
129/// // log_field("field_int") is called automatically
130/// }
131/// ```
132///
133/// ## Field attributes
134/// ### `#[patch(skip)]`
135/// If you want certain fields to be unpatchable, you can let the derive macro skip certain fields when creating the patch struct
136/// ```rust
137/// # use struct_patch::Patch;
138/// #[derive(Patch)]
139/// struct Item {
140/// #[patch(skip)]
141/// id: String,
142/// data: String,
143/// }
144///
145/// // Generated struct
146/// // struct ItemPatch {
147/// // data: Option<String>,
148/// // }
149/// ```
150///
151/// ### `#[patch(skip_wrap)]`
152/// Keep the field type as-is in the generated patch struct (no extra `Option`
153/// wrapping). This is useful for fields that are already `Option<...>`,
154/// typically `Option<Vec<_>>`, where the default double-`Option` in the patch
155/// is unwanted. With `skip_wrap`, `None` in the patch means "no change" and
156/// `Some(v)` sets the field to `Some(v)` (including `Some(vec![])` to clear
157/// the vector). Cannot be combined with `empty_value`.
158/// ```rust
159/// # use struct_patch::Patch;
160/// #[derive(Default, Patch)]
161/// struct Item {
162/// #[patch(skip_wrap)]
163/// tags: Option<Vec<String>>,
164/// }
165///
166/// // Generated struct
167/// // struct ItemPatch {
168/// // tags: Option<Vec<String>>, // not wrapped again
169/// // }
170///
171/// let mut item = Item { tags: Some(vec!["a".into()]) };
172///
173/// // `None` in the patch keeps the field unchanged.
174/// item.apply(ItemPatch { tags: None });
175/// assert_eq!(item.tags, Some(vec!["a".into()]));
176///
177/// // `Some(vec![])` still applies and clears the list.
178/// item.apply(ItemPatch { tags: Some(vec![]) });
179/// assert_eq!(item.tags, Some(vec![]));
180/// ```
181pub trait Patch<P> {
182 /// Apply a patch
183 fn apply(&mut self, patch: P);
184
185 /// Apply a patch, calling `log` with each patched field name.
186 ///
187 /// The default implementation ignores `log` and delegates to [`apply`](Patch::apply).
188 /// The derive macro generates an override that calls `log` once per field that is
189 /// actually changed.
190 ///
191 /// When the `nesting` feature is enabled, `log` receives both a prefix path and field name.
192 /// When `nesting` is disabled, `log` receives only the field name.
193 ///
194 /// ```rust
195 /// # use struct_patch::Patch;
196 /// #[derive(Default, Patch)]
197 /// struct Item {
198 /// field_int: usize,
199 /// field_string: String,
200 /// }
201 ///
202 /// let mut item = Item::default();
203 /// let patch = ItemPatch { field_int: Some(42), field_string: None };
204 ///
205 /// let mut patched_fields = Vec::new();
206 /// #[cfg(feature = "simple-nesting")]
207 /// item.apply_with_log(patch, |prefix, field| {
208 /// let path = if prefix.is_empty() {
209 /// field.to_string()
210 /// } else {
211 /// format!("{}.{}", prefix, field)
212 /// };
213 /// patched_fields.push(path);
214 /// });
215 /// #[cfg(all(feature = "nesting", not(feature = "simple-nesting")))]
216 /// item.apply_with_log(patch, |prefixes, field| {
217 /// let path = if prefixes.is_empty() {
218 /// field.to_string()
219 /// } else {
220 /// format!("{}.{}", prefixes.join("."), field)
221 /// };
222 /// patched_fields.push(path);
223 /// });
224 ///
225 /// #[cfg(not(feature = "nesting"))]
226 /// item.apply_with_log(patch, |field| {
227 /// patched_fields.push(field.to_string());
228 /// });
229 ///
230 /// assert_eq!(patched_fields, vec!["field_int"]);
231 /// ```
232 #[cfg(all(feature = "log", feature = "simple-nesting"))]
233 fn apply_with_log<F: FnMut(&str, &str)>(&mut self, patch: P, _log: F) {
234 self.apply(patch);
235 }
236 #[cfg(all(feature = "log", feature = "nesting", not(feature = "simple-nesting")))]
237 fn apply_with_log<F: FnMut(&[&str], &str)>(&mut self, patch: P, _log: F) {
238 self.apply(patch);
239 }
240
241 #[cfg(all(feature = "log", not(feature = "nesting"), not(feature = "simple-nesting")))]
242 fn apply_with_log<F: FnMut(&str)>(&mut self, patch: P, _log: F) {
243 self.apply(patch);
244 }
245
246 /// Returns a patch that when applied turns any struct of the same type into `Self`
247 fn into_patch(self) -> P;
248
249 /// Returns a patch that when applied turns `previous_struct` into `Self`
250 fn into_patch_by_diff(self, previous_struct: Self) -> P;
251
252 /// Get an empty patch instance
253 fn new_empty_patch() -> P;
254}
255
256pub trait Filler<F> {
257 /// Apply a filler
258 fn apply(&mut self, filler: F);
259
260 /// Apply a filler, calling `log` with each field name that is actually filled.
261 ///
262 /// The default implementation ignores `log` and delegates to [`apply`](Filler::apply).
263 /// The derive macro generates an override that calls `log` once per field that is
264 /// actually filled (i.e. the field was empty and the filler supplied a value).
265 ///
266 /// When the `nesting` feature is enabled, `log` receives both a prefix path and field name.
267 /// When `nesting` is disabled, `log` receives only the field name.
268 ///
269 /// ```rust
270 /// # use struct_patch::Filler;
271 /// #[derive(Default, Filler)]
272 /// struct Item {
273 /// value: Option<usize>,
274 /// }
275 ///
276 /// let mut item = Item::default();
277 /// let filler = ItemFiller { value: Some(42) };
278 ///
279 /// let mut filled_fields = Vec::new();
280 /// #[cfg(feature = "nesting")]
281 /// item.apply_with_log(filler, |prefixes, field| {
282 /// let path = if prefixes.is_empty() {
283 /// field.to_string()
284 /// } else {
285 /// format!("{}.{}", prefixes.join("."), field)
286 /// };
287 /// filled_fields.push(path);
288 /// });
289 ///
290 /// #[cfg(not(feature = "nesting"))]
291 /// item.apply_with_log(filler, |field| {
292 /// filled_fields.push(field.to_string());
293 /// });
294 ///
295 /// assert_eq!(filled_fields, vec!["value"]);
296 /// ```
297 #[cfg(all(feature = "log", feature = "simple-nesting"))]
298 fn apply_with_log<L: FnMut(&str, &str)>(&mut self, filler: F, _log: L) {
299 self.apply(filler);
300 }
301
302 #[cfg(all(feature = "log", feature = "nesting", not(feature = "simple-nesting")))]
303 fn apply_with_log<L: FnMut(&[&str], &str)>(&mut self, filler: F, _log: L) {
304 self.apply(filler);
305 }
306
307 #[cfg(all(feature = "log", not(feature = "nesting"), not(feature = "simple-nesting")))]
308 fn apply_with_log<L: FnMut(&str)>(&mut self, filler: F, _log: L) {
309 self.apply(filler);
310 }
311
312 /// Get an empty filler instance
313 fn new_empty_filler() -> F;
314}
315
316#[cfg(feature = "status")]
317/// A patch struct with extra status information
318pub trait Status {
319 /// Returns `true` if all fields are `None`, `false` otherwise.
320 fn is_empty(&self) -> bool;
321}
322
323#[cfg(feature = "merge")]
324/// A patch struct that can be merged to another one
325pub trait Merge {
326 fn merge(self, other: Self) -> Self;
327}
328
329#[cfg(feature = "substrate")]
330/// A substrate struct that can expose the fields information thereof
331pub trait Substrate {
332 fn expose_content() -> &'static str;
333
334 /// Expose the field information, by call this function in Build.rs
335 fn expose();
336}
337
338#[cfg(feature = "catalyst")]
339/// A catalyst struct that can expose the fields information thereof
340pub trait Catalyst<S, Cpx> {
341 /// catalyst bind on substrate and generate complex
342 fn bind(self, substrate: S) -> Cpx;
343}
344
345#[cfg(feature = "catalyst")]
346/// A complex struct that can decouple return catalyst and substrate
347pub trait Complex<Cat, S> {
348 /// complex decouple to catalyst and substrate
349 fn decouple(self) -> (Cat, S);
350}