Skip to content

Commit 698c832

Browse files
committed
add op feature for filler
1 parent 4e8037a commit 698c832

File tree

5 files changed

+175
-26
lines changed

5 files changed

+175
-26
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ The [examples][examples] demo following scenarios.
132132
## Features
133133
This crate also includes the following optional features:
134134
- `status`(default): implements the `Status` trait for the patch struct, which provides the `is_empty` method.
135-
- `op` (default): provide operators `<<` between instance and patch, and `+` for patches
136-
- 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.
135+
- `op` (default): provide operators `<<` between instance and patch/filler, and `+` for patches/fillers
136+
- 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.
137137
- `merge` (optional): implements the `Merge` trait for the patch struct, which provides the `merge` method, and `<<` (if `op` feature enabled) between patches.
138138
- `std`(optional):
139139
- `box`: implements the `Patch<Box<P>>` trait for `T` where `T` implements `Patch<P>`.

derive/src/filler.rs

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ 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+
#[cfg(feature = "op")]
16+
const ADDABLE: &str = "addable";
1217

1318
pub(crate) struct Filler {
1419
visibility: syn::Visibility,
@@ -49,6 +54,8 @@ struct Field {
4954
ty: Type,
5055
attributes: Vec<TokenStream>,
5156
fty: FillerType,
57+
#[cfg(feature = "op")]
58+
addable: Addable,
5259
}
5360

5461
impl Filler {
@@ -74,6 +81,13 @@ impl Filler {
7481
.map(|f| f.ident.as_ref())
7582
.collect::<Vec<_>>();
7683

84+
#[cfg(feature = "op")]
85+
let option_field_names_addable = fields
86+
.iter()
87+
.filter(|f| matches!(f.fty, FillerType::Option))
88+
.map(|f| !matches!(f.addable, Addable::Disable))
89+
.collect::<Vec<_>>();
90+
7791
let extendable_field_names = fields
7892
.iter()
7993
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
@@ -86,18 +100,32 @@ impl Filler {
86100
.map(|f| f.fty.inner())
87101
.collect::<Vec<_>>();
88102

103+
#[cfg(feature = "op")]
104+
let extendable_field_addable = fields
105+
.iter()
106+
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
107+
.map(|f| !matches!(f.addable, Addable::Disable))
108+
.collect::<Vec<_>>();
109+
89110
let native_value_field_names = fields
90111
.iter()
91112
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
92113
.map(|f| f.ident.as_ref())
93114
.collect::<Vec<_>>();
94115

95-
let native_value_field_values = fields
116+
let native_value_field_empty_values = fields
96117
.iter()
97118
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
98119
.map(|f| f.fty.value())
99120
.collect::<Vec<_>>();
100121

122+
#[cfg(feature = "op")]
123+
let native_value_field_addable = fields
124+
.iter()
125+
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
126+
.map(|f| !matches!(f.addable, Addable::Disable))
127+
.collect::<Vec<_>>();
128+
101129
let mapped_attributes = attributes
102130
.iter()
103131
.map(|a| {
@@ -130,7 +158,7 @@ impl Filler {
130158
}
131159
)*
132160
#(
133-
if self.#native_value_field_names != #native_value_field_values {
161+
if self.#native_value_field_names != #native_value_field_empty_values {
134162
return false
135163
}
136164
)*
@@ -141,11 +169,67 @@ impl Filler {
141169
#[cfg(not(feature = "status"))]
142170
let status_impl = quote!();
143171

172+
#[cfg(feature = "op")]
173+
let op_impl = quote! {
174+
impl #generics core::ops::Shl<#name #generics> for #struct_name #generics #where_clause {
175+
type Output = Self;
176+
177+
fn shl(mut self, rhs: #name #generics) -> Self {
178+
struct_patch::traits::Filler::apply(&mut self, rhs);
179+
self
180+
}
181+
}
182+
183+
impl #generics core::ops::Add<Self> for #name #generics #where_clause {
184+
type Output = Self;
185+
186+
fn add(mut self, rhs: Self) -> Self {
187+
#(
188+
if self.#native_value_field_names == #native_value_field_empty_values {
189+
self.#native_value_field_names = rhs.#native_value_field_names;
190+
} else if #native_value_field_addable {
191+
self.#native_value_field_names = self.#native_value_field_names + rhs.#native_value_field_names;
192+
} else {
193+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#native_value_field_names))
194+
}
195+
)*
196+
#(
197+
if self.#extendable_field_names.is_empty() {
198+
self.#extendable_field_names = rhs.#extendable_field_names;
199+
} else if #extendable_field_addable {
200+
self.#extendable_field_names.extend(rhs.#extendable_field_names.into_iter());
201+
} else {
202+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#extendable_field_names))
203+
}
204+
)*
205+
#(
206+
if let Some(b) = self.#option_field_names {
207+
if let Some(a) = rhs.#option_field_names {
208+
if #option_field_names_addable {
209+
self.#option_field_names = Some(a + &b);
210+
} else {
211+
panic!("`{}` conflict in fillers, please use `#[filler(addable)]`", stringify!(#option_field_names))
212+
}
213+
} else {
214+
self.#option_field_names = Some(b);
215+
}
216+
} else {
217+
self.#option_field_names = rhs.#option_field_names;
218+
}
219+
)*
220+
self
221+
}
222+
}
223+
};
224+
225+
#[cfg(not(feature = "op"))]
226+
let op_impl = quote!();
227+
144228
let filler_impl = quote! {
145229
impl #generics struct_patch::traits::Filler< #name #generics > for #struct_name #generics #where_clause {
146230
fn apply(&mut self, filler: #name #generics) {
147231
#(
148-
if self.#native_value_field_names == #native_value_field_values {
232+
if self.#native_value_field_names == #native_value_field_empty_values {
149233
self.#native_value_field_names = filler.#native_value_field_names;
150234
}
151235
)*
@@ -167,7 +251,7 @@ impl Filler {
167251
#name {
168252
#(#option_field_names: None,)*
169253
#(#extendable_field_names: #extendable_field_types::default(),)*
170-
#(#native_value_field_names: #native_value_field_values,)*
254+
#(#native_value_field_names: #native_value_field_empty_values,)*
171255
}
172256
}
173257
}
@@ -177,6 +261,7 @@ impl Filler {
177261
#filler_struct
178262
#status_impl
179263
#filler_impl
264+
#op_impl
180265
})
181266
}
182267

@@ -293,6 +378,8 @@ impl Field {
293378
) -> Result<Option<Field>> {
294379
let mut fty = filler_type(&ty);
295380
let mut attributes = vec![];
381+
#[cfg(feature = "op")]
382+
let mut addable = Addable::Disable;
296383

297384
for attr in attrs {
298385
if attr.path().to_string().as_str() != FILLER {
@@ -336,6 +423,18 @@ impl Field {
336423
.error("empty_value needs a clear value to define empty"));
337424
}
338425
}
426+
#[cfg(feature = "op")]
427+
ADDABLE => {
428+
// #[filler(addable)]
429+
addable = Addable::AddTrait;
430+
}
431+
#[cfg(not(feature = "op"))]
432+
ADDABLE => {
433+
return Err(syn::Error::new(
434+
ident.span(),
435+
"`addable` needs `op` feature",
436+
));
437+
}
339438
_ => {
340439
return Err(meta.error(format_args!(
341440
"unknown filler field attribute `{}`",
@@ -352,6 +451,8 @@ impl Field {
352451
ty,
353452
attributes,
354453
fty,
454+
#[cfg(feature = "op")]
455+
addable,
355456
}))
356457
}
357458
}

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))

derive/src/patch.rs

Lines changed: 20 additions & 17 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";
@@ -24,14 +27,6 @@ pub(crate) struct Patch {
2427
fields: Vec<Field>,
2528
}
2629

27-
#[cfg(feature = "op")]
28-
pub(crate) enum Addable {
29-
Disable,
30-
AddTriat,
31-
#[cfg(feature = "op")]
32-
AddFn(Ident),
33-
}
34-
3530
struct Field {
3631
ident: Option<Ident>,
3732
ty: Type,
@@ -66,7 +61,8 @@ impl Patch {
6661
let field_names = fields
6762
.iter()
6863
.filter(|f| !f.nesting)
69-
.map(|f| f.ident.as_ref()).collect::<Vec<_>>();
64+
.map(|f| f.ident.as_ref())
65+
.collect::<Vec<_>>();
7066

7167
#[cfg(not(feature = "nesting"))]
7268
let renamed_field_names = fields
@@ -182,8 +178,8 @@ impl Patch {
182178
.iter()
183179
.map(|f| {
184180
match &f.addable {
185-
Addable::AddTriat => quote!(
186-
Some(a + b)
181+
Addable::AddTrait => quote!(
182+
Some(a + &b)
187183
),
188184
Addable::AddFn(f) => {
189185
quote!(
@@ -495,7 +491,10 @@ impl Field {
495491
Some(ident) => {
496492
if *nesting {
497493
// TODO handle rename
498-
let patch_type = syn::Ident::new(&format!("{}Patch", &ty.to_token_stream()), Span::call_site());
494+
let patch_type = syn::Ident::new(
495+
&format!("{}Patch", &ty.to_token_stream()),
496+
Span::call_site(),
497+
);
499498
Ok(quote! {
500499
#(#attributes)*
501500
pub #ident: #patch_type,
@@ -506,7 +505,7 @@ impl Field {
506505
pub #ident: Option<#ty>,
507506
})
508507
}
509-
},
508+
}
510509
#[cfg(not(feature = "nesting"))]
511510
None => Ok(quote! {
512511
#(#attributes)*
@@ -516,7 +515,10 @@ impl Field {
516515
None => {
517516
if *nesting {
518517
// TODO handle rename
519-
let patch_type = syn::Ident::new(&format!("{}Patch", &ty.to_token_stream()), Span::call_site());
518+
let patch_type = syn::Ident::new(
519+
&format!("{}Patch", &ty.to_token_stream()),
520+
Span::call_site(),
521+
);
520522
Ok(quote! {
521523
#(#attributes)*
522524
pub #patch_type,
@@ -527,7 +529,7 @@ impl Field {
527529
pub Option<#ty>,
528530
})
529531
}
530-
},
532+
}
531533
}
532534
}
533535

@@ -579,7 +581,7 @@ impl Field {
579581
#[cfg(feature = "op")]
580582
ADDABLE => {
581583
// #[patch(addable)]
582-
addable = Addable::AddTriat;
584+
addable = Addable::AddTrait;
583585
}
584586
#[cfg(not(feature = "op"))]
585587
ADDABLE => {
@@ -606,7 +608,8 @@ impl Field {
606608
#[cfg(not(feature = "nesting"))]
607609
NESTING => {
608610
return Err(
609-
meta.error("#[patch(nesting)] only work with `nesting` feature"));
611+
meta.error("#[patch(nesting)] only work with `nesting` feature")
612+
);
610613
}
611614
_ => {
612615
return Err(meta.error(format_args!(

0 commit comments

Comments
 (0)