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/// ## Field attributes
64/// ### `#[patch(skip)]`
65/// If you want certain fields to be unpatchable, you can let the derive macro skip certain fields when creating the patch struct
66/// ```rust
67/// # use struct_patch::Patch;
68/// #[derive(Patch)]
69/// struct Item {
70/// #[patch(skip)]
71/// id: String,
72/// data: String,
73/// }
74///
75/// // Generated struct
76/// // struct ItemPatch {
77/// // data: Option<String>,
78/// // }
79/// ```
80///
81/// ### `#[patch(skip_wrap)]`
82/// Keep the field type as-is in the generated patch struct (no extra `Option`
83/// wrapping). This is useful for fields that are already `Option<...>`,
84/// typically `Option<Vec<_>>`, where the default double-`Option` in the patch
85/// is unwanted. With `skip_wrap`, `None` in the patch means "no change" and
86/// `Some(v)` sets the field to `Some(v)` (including `Some(vec![])` to clear
87/// the vector). Cannot be combined with `empty_value`.
88/// ```rust
89/// # use struct_patch::Patch;
90/// #[derive(Default, Patch)]
91/// struct Item {
92/// #[patch(skip_wrap)]
93/// tags: Option<Vec<String>>,
94/// }
95///
96/// // Generated struct
97/// // struct ItemPatch {
98/// // tags: Option<Vec<String>>, // not wrapped again
99/// // }
100///
101/// let mut item = Item { tags: Some(vec!["a".into()]) };
102///
103/// // `None` in the patch keeps the field unchanged.
104/// item.apply(ItemPatch { tags: None });
105/// assert_eq!(item.tags, Some(vec!["a".into()]));
106///
107/// // `Some(vec![])` still applies and clears the list.
108/// item.apply(ItemPatch { tags: Some(vec![]) });
109/// assert_eq!(item.tags, Some(vec![]));
110/// ```
111pub trait Patch<P> {
112 /// Apply a patch
113 fn apply(&mut self, patch: P);
114
115 /// Returns a patch that when applied turns any struct of the same type into `Self`
116 fn into_patch(self) -> P;
117
118 /// Returns a patch that when applied turns `previous_struct` into `Self`
119 fn into_patch_by_diff(self, previous_struct: Self) -> P;
120
121 /// Get an empty patch instance
122 fn new_empty_patch() -> P;
123}
124
125pub trait Filler<F> {
126 /// Apply a filler
127 fn apply(&mut self, filler: F);
128
129 /// Get an empty filler instance
130 fn new_empty_filler() -> F;
131}
132
133#[cfg(feature = "status")]
134/// A patch struct with extra status information
135pub trait Status {
136 /// Returns `true` if all fields are `None`, `false` otherwise.
137 fn is_empty(&self) -> bool;
138}
139
140#[cfg(feature = "merge")]
141/// A patch struct that can be merged to another one
142pub trait Merge {
143 fn merge(self, other: Self) -> Self;
144}
145
146#[cfg(feature = "catalyst")]
147/// A substrate struct that can expose the fields information thereof
148pub trait Substrate {
149 fn expose_content() -> &'static str;
150
151 /// Expose the field information, by call this function in Build.rs
152 fn expose();
153}
154
155#[cfg(feature = "catalyst")]
156/// A catalyst struct that can expose the fields information thereof
157pub trait Catalyst<S, Cpx> {
158 /// catalyst bind on substrate and generate complex
159 fn bind(self, substrate: S) -> Cpx;
160}
161
162#[cfg(feature = "catalyst")]
163/// A complex struct that can decouple return catalyst and substrate
164pub trait Complex<Cat, S> {
165 /// complex decouple to catalyst and substrate
166 fn decouple(self) -> (Cat, S);
167}