Skip to content

Commit 029fa77

Browse files
committed
add op feature for filler
1 parent b1a1eea commit 029fa77

File tree

5 files changed

+161
-21
lines changed

5 files changed

+161
-21
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ The [examples][examples] demo following scenarios.
131131
## Features
132132
This crate also includes the following optional features:
133133
- `status`(default): implements the `Status` trait for the patch struct, which provides the `is_empty` method.
134-
- `op` (default): provide operators `<<` between instance and patch, and `+` for patches
135-
- default: when there is a field conflict between patches, `+` will add together if the `#[patch(addable)]` or `#[patch(add=fn)]` is provided, else it will panic.
134+
- `op` (default): provide operators `<<` between instance and patch/filler, and `+` for patches/fillers
135+
- default: when there is a field conflict between patches/fillers, `+` will add together if the `#[patch(addable)]`, `#[patch(add=fn)]` or `#[filler(addable)]` is provided, else it will panic.
136136
- `merge` (optional): implements the `Merge` trait for the patch struct, which provides the `merge` method, and `<<` (if `op` feature enabled) between patches.
137137
- `std`(optional):
138138
- `box`: implements the `Patch<Box<P>>` trait for `T` where `T` implements `Patch<P>`.

struct-patch-derive/src/filler.rs

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ use syn::meta::ParseNestedMeta;
55
use syn::spanned::Spanned;
66
use syn::{parenthesized, DeriveInput, Error, Lit, LitStr, Result, Type};
77

8+
#[cfg(feature = "op")]
9+
use crate::Addable;
10+
811
const FILLER: &str = "filler";
912
const ATTRIBUTE: &str = "attribute";
1013
const EXTENDABLE: &str = "extendable";
1114
const EMPTY_VALUE: &str = "empty_value";
15+
const ADDABLE: &str = "addable";
1216

1317
pub(crate) struct Filler {
1418
visibility: syn::Visibility,
@@ -49,6 +53,8 @@ struct Field {
4953
ty: Type,
5054
attributes: Vec<TokenStream>,
5155
fty: FillerType,
56+
#[cfg(feature = "op")]
57+
addable: Addable,
5258
}
5359

5460
impl Filler {
@@ -74,6 +80,13 @@ impl Filler {
7480
.map(|f| f.ident.as_ref())
7581
.collect::<Vec<_>>();
7682

83+
#[cfg(feature = "op")]
84+
let option_field_names_addable = fields
85+
.iter()
86+
.filter(|f| matches!(f.fty, FillerType::Option))
87+
.map(|f| !matches!(f.addable, Addable::Disable))
88+
.collect::<Vec<_>>();
89+
7790
let extendable_field_names = fields
7891
.iter()
7992
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
@@ -86,18 +99,32 @@ impl Filler {
8699
.map(|f| f.fty.inner())
87100
.collect::<Vec<_>>();
88101

102+
#[cfg(feature = "op")]
103+
let extendable_field_addable = fields
104+
.iter()
105+
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
106+
.map(|f| !matches!(f.addable, Addable::Disable))
107+
.collect::<Vec<_>>();
108+
89109
let native_value_field_names = fields
90110
.iter()
91111
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
92112
.map(|f| f.ident.as_ref())
93113
.collect::<Vec<_>>();
94114

95-
let native_value_field_values = fields
115+
let native_value_field_empty_values = fields
96116
.iter()
97117
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
98118
.map(|f| f.fty.value())
99119
.collect::<Vec<_>>();
100120

121+
#[cfg(feature = "op")]
122+
let native_value_field_addable = fields
123+
.iter()
124+
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
125+
.map(|f| !matches!(f.addable, Addable::Disable))
126+
.collect::<Vec<_>>();
127+
101128
let mapped_attributes = attributes
102129
.iter()
103130
.map(|a| {
@@ -130,7 +157,7 @@ impl Filler {
130157
}
131158
)*
132159
#(
133-
if self.#native_value_field_names != #native_value_field_values {
160+
if self.#native_value_field_names != #native_value_field_empty_values {
134161
return false
135162
}
136163
)*
@@ -141,11 +168,67 @@ impl Filler {
141168
#[cfg(not(feature = "status"))]
142169
let status_impl = quote!();
143170

171+
#[cfg(feature = "op")]
172+
let op_impl = quote! {
173+
impl #generics core::ops::Shl<#name #generics> for #struct_name #generics #where_clause {
174+
type Output = Self;
175+
176+
fn shl(mut self, rhs: #name #generics) -> Self {
177+
struct_patch::traits::Filler::apply(&mut self, rhs);
178+
self
179+
}
180+
}
181+
182+
impl #generics core::ops::Add<Self> for #name #generics #where_clause {
183+
type Output = Self;
184+
185+
fn add(mut self, rhs: Self) -> Self {
186+
#(
187+
if self.#native_value_field_names == #native_value_field_empty_values {
188+
self.#native_value_field_names = rhs.#native_value_field_names;
189+
} else if #native_value_field_addable {
190+
self.#native_value_field_names = self.#native_value_field_names + rhs.#native_value_field_names;
191+
} else {
192+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#native_value_field_names))
193+
}
194+
)*
195+
#(
196+
if self.#extendable_field_names.is_empty() {
197+
self.#extendable_field_names = rhs.#extendable_field_names;
198+
} else if #extendable_field_addable {
199+
self.#extendable_field_names.extend(rhs.#extendable_field_names.into_iter());
200+
} else {
201+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#extendable_field_names))
202+
}
203+
)*
204+
#(
205+
if let Some(b) = self.#option_field_names {
206+
if let Some(a) = rhs.#option_field_names {
207+
if #option_field_names_addable {
208+
self.#option_field_names = Some(a + &b);
209+
} else {
210+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#option_field_names))
211+
}
212+
} else {
213+
self.#option_field_names = Some(b);
214+
}
215+
} else {
216+
self.#option_field_names = rhs.#option_field_names;
217+
}
218+
)*
219+
self
220+
}
221+
}
222+
};
223+
224+
#[cfg(not(feature = "op"))]
225+
let op_impl = quote!();
226+
144227
let filler_impl = quote! {
145228
impl #generics struct_patch::traits::Filler< #name #generics > for #struct_name #generics #where_clause {
146229
fn apply(&mut self, filler: #name #generics) {
147230
#(
148-
if self.#native_value_field_names == #native_value_field_values {
231+
if self.#native_value_field_names == #native_value_field_empty_values {
149232
self.#native_value_field_names = filler.#native_value_field_names;
150233
}
151234
)*
@@ -167,7 +250,7 @@ impl Filler {
167250
#name {
168251
#(#option_field_names: None,)*
169252
#(#extendable_field_names: #extendable_field_types::default(),)*
170-
#(#native_value_field_names: #native_value_field_values,)*
253+
#(#native_value_field_names: #native_value_field_empty_values,)*
171254
}
172255
}
173256
}
@@ -177,6 +260,7 @@ impl Filler {
177260
#filler_struct
178261
#status_impl
179262
#filler_impl
263+
#op_impl
180264
})
181265
}
182266

@@ -293,6 +377,8 @@ impl Field {
293377
) -> Result<Option<Field>> {
294378
let mut fty = filler_type(&ty);
295379
let mut attributes = vec![];
380+
#[cfg(feature = "op")]
381+
let mut addable = Addable::Disable;
296382

297383
for attr in attrs {
298384
if attr.path().to_string().as_str() != FILLER {
@@ -336,9 +422,21 @@ impl Field {
336422
.error("empty_value needs a clear value to define empty"));
337423
}
338424
}
425+
#[cfg(feature = "op")]
426+
ADDABLE => {
427+
// #[filler(addable)]
428+
addable = Addable::AddTrait;
429+
}
430+
#[cfg(not(feature = "op"))]
431+
ADDABLE => {
432+
return Err(syn::Error::new(
433+
ident.span(),
434+
"`addable` needs `op` feature",
435+
));
436+
},
339437
_ => {
340438
return Err(meta.error(format_args!(
341-
"unknown filler field attribute `{}`",
439+
"unknown patch field attribute `{}`",
342440
path.replace(' ', "")
343441
)));
344442
}
@@ -352,6 +450,8 @@ impl Field {
352450
ty,
353451
attributes,
354452
fty,
453+
#[cfg(feature = "op")]
454+
addable,
355455
}))
356456
}
357457
}

struct-patch-derive/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@ extern crate proc_macro;
22
mod filler;
33
mod patch;
44

5+
use proc_macro2::Ident;
6+
57
use filler::Filler;
68
use patch::Patch;
79

10+
#[cfg(feature = "op")]
11+
pub(crate) enum Addable {
12+
Disable,
13+
AddTrait,
14+
AddFn(Ident),
15+
}
16+
817
#[proc_macro_derive(Patch, attributes(patch))]
918
pub fn derive_patch(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
1019
Patch::from_ast(syn::parse_macro_input!(item as syn::DeriveInput))

struct-patch-derive/src/patch.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use syn::{
77
Type,
88
};
99

10+
#[cfg(feature = "op")]
11+
use crate::Addable;
12+
1013
const PATCH: &str = "patch";
1114
const NAME: &str = "name";
1215
const ATTRIBUTE: &str = "attribute";
@@ -23,14 +26,6 @@ pub(crate) struct Patch {
2326
fields: Vec<Field>,
2427
}
2528

26-
#[cfg(feature = "op")]
27-
pub(crate) enum Addable {
28-
Disable,
29-
AddTriat,
30-
#[cfg(feature = "op")]
31-
AddFn(Ident),
32-
}
33-
3429
struct Field {
3530
ident: Option<Ident>,
3631
ty: Type,
@@ -131,8 +126,8 @@ impl Patch {
131126
.iter()
132127
.map(|f| {
133128
match &f.addable {
134-
Addable::AddTriat => quote!(
135-
Some(a + b)
129+
Addable::AddTrait => quote!(
130+
Some(a + &b)
136131
),
137132
Addable::AddFn(f) => {
138133
quote!(
@@ -472,7 +467,7 @@ impl Field {
472467
#[cfg(feature = "op")]
473468
ADDABLE => {
474469
// #[patch(addable)]
475-
addable = Addable::AddTriat;
470+
addable = Addable::AddTrait;
476471
}
477472
#[cfg(not(feature = "op"))]
478473
ADDABLE => {

struct-patch/examples/filler.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use struct_patch::Filler;
55
use struct_patch::Status;
66

77
// NOTE: Default, Extend, IntoIterator, is_empty are required for extendable type
8-
#[derive(Debug, Default)]
8+
#[derive(Clone, Debug, Default)]
99
struct WrapVec {
1010
inner: Vec<usize>,
1111
}
@@ -30,16 +30,20 @@ impl WrapVec {
3030
}
3131
}
3232

33-
#[derive(Default, Filler)]
34-
#[filler(attribute(derive(Debug, Default)))]
33+
#[derive(Clone, Default, Filler)]
34+
#[filler(attribute(derive(Clone, Debug, Default)))]
3535
struct Item {
3636
field_complete: bool,
3737
// Will check the field is equal to the value to define the field is empty or not
3838
#[filler(empty_value = 0)]
39+
// Will allow field add each other when `+` on fillers
40+
#[filler(addable)]
3941
field_int: usize,
4042
field_string: String,
4143
maybe_field_int: Option<usize>,
4244
maybe_field_string: Option<String>,
45+
// Will allow field extend each other when `+` on fillers
46+
#[filler(addable)]
4347
list: Vec<usize>,
4448
_deque: VecDeque<usize>,
4549
_linked_list: LinkedList<usize>,
@@ -127,4 +131,36 @@ fn main() {
127131
filler.field_int = 5;
128132
item.apply(filler);
129133
assert_eq!(item.field_int, 7);
134+
135+
#[cfg(feature = "op")]
136+
{
137+
let item = Item::default();
138+
139+
let mut filler1: ItemFiller = Item::new_empty_filler();
140+
filler1.field_int = 7;
141+
filler1.list = vec![1, 2];
142+
143+
let mut filler2: ItemFiller = Item::new_empty_filler();
144+
filler2.field_int = 8;
145+
filler2.list = vec![3, 4];
146+
filler2.maybe_field_string = Some("Something".into());
147+
148+
let final_item_from_added_fillers = item.clone() << (filler1.clone() + filler2.clone());
149+
150+
assert_eq!(final_item_from_added_fillers.field_int, 15);
151+
assert_eq!(final_item_from_added_fillers.list, vec![1, 2, 3, 4]);
152+
assert_eq!(
153+
final_item_from_added_fillers.maybe_field_string,
154+
Some("Something".into())
155+
);
156+
157+
let final_item_after_fillers_applied = item << filler1 << filler2;
158+
159+
assert_eq!(final_item_after_fillers_applied.field_int, 7);
160+
assert_eq!(final_item_after_fillers_applied.list, vec![1, 2]);
161+
assert_eq!(
162+
final_item_after_fillers_applied.maybe_field_string,
163+
Some("Something".into())
164+
);
165+
}
130166
}

0 commit comments

Comments
 (0)