Skip to content

Commit d62efff

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

File tree

7 files changed

+202
-27
lines changed

7 files changed

+202
-27
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
run: |
3434
nix develop .#ci -c cargo run --features=std --example instance
3535
nix develop .#ci -c cargo run --features=std --example filler
36+
nix develop .#ci -c cargo run --features=std --example filler-op
3637
nix develop .#ci -c cargo run --features=std --example diff
3738
nix develop .#ci -c cargo run --features=std --example json
3839
nix develop .#ci -c cargo run --features=std --example rename-patch-struct

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 & 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 if rhs.#native_value_field_names != #native_value_field_empty_values {
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 if !rhs.#extendable_field_names.is_empty() {
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
}

derive/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ mod patch;
55
use filler::Filler;
66
use patch::Patch;
77

8+
#[cfg(feature = "op")]
9+
pub(crate) enum Addable {
10+
Disable,
11+
AddTrait,
12+
AddFn(proc_macro2::Ident),
13+
}
14+
815
#[proc_macro_derive(Patch, attributes(patch))]
916
pub fn derive_patch(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
1017
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)