Skip to content

Commit 9ba53ef

Browse files
authored
Handle extenable types (#90)
* handle extendable types in collections * implement `#[filler(extendable)]` * bounce version * readme
1 parent a298cb5 commit 9ba53ef

File tree

6 files changed

+110
-24
lines changed

6 files changed

+110
-24
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77

88
[workspace.package]
99
authors = ["Antonio Yang <yanganto@gmail.com>"]
10-
version = "0.9.1"
10+
version = "0.9.2"
1111
edition = "2021"
1212
categories = ["development-tools"]
1313
keywords = ["struct", "patch", "macro", "derive", "overlay"]

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ assert_eq!(item.list, vec![7]);
100100
```
101101

102102
## Documentation and Examples
103-
Also, you can modify the patch structure by defining `#[patch(...)]` attributes on the original struct or fields.
103+
Also, you can modify the patch structure by defining `#[patch(...)]` or `#[filler(...)]` attributes on the original struct or fields.
104104

105105
Struct attributes:
106106
- `#[patch(name = "...")]`: change the name of the generated patch struct.
@@ -112,6 +112,7 @@ Field attributes:
112112
- `#[patch(name = "...")]`: change the type of the field in the generated patch struct.
113113
- `#[patch(attribute(...))]`: add attributes to the field in the generated patch struct.
114114
- `#[patch(attribute(derive(...)))]`: add derives to the field in the generated patch struct.
115+
- `#[filler(extendable)]`: use the struct of field for Filler, the struct needs implement `Default`, `Extend`, `IntoIterator` and `is_empty`.
115116

116117
Please check the [traits][doc-traits] of document to learn more.
117118

@@ -124,6 +125,7 @@ The [examples][examples] demo following scenarios.
124125
- show option field behavior
125126
- show operators about patches
126127
- show example with serde crates, ex: `humantime_serde` for duration
128+
- show filler with all possible types
127129

128130
## Features
129131
This crate also includes the following optional features:

struct-patch-derive/src/filler.rs

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use syn::{parenthesized, DeriveInput, LitStr, Result, Type};
55

66
const FILLER: &str = "filler";
77
const ATTRIBUTE: &str = "attribute";
8+
const EXTENDABLE: &str = "extendable";
89

910
pub(crate) struct Filler {
1011
visibility: syn::Visibility,
@@ -18,7 +19,17 @@ pub(crate) struct Filler {
1819
#[derive(Debug, PartialEq)]
1920
enum FillerType {
2021
Option,
21-
Vec,
22+
Extendable(Ident),
23+
}
24+
25+
impl FillerType {
26+
fn inner(&self) -> &Ident {
27+
if let FillerType::Extendable(ident) = self {
28+
ident
29+
} else {
30+
panic!("FillerType::Option has no inner indent")
31+
}
32+
}
2233
}
2334

2435
struct Field {
@@ -45,18 +56,24 @@ impl Filler {
4556
.map(|f| f.to_token_stream())
4657
.collect::<Result<Vec<_>>>()?;
4758

48-
let vector_field_names = fields
59+
let option_field_names = fields
4960
.iter()
50-
.filter(|f| f.fty == FillerType::Vec)
61+
.filter(|f| f.fty == FillerType::Option)
5162
.map(|f| f.ident.as_ref())
5263
.collect::<Vec<_>>();
5364

54-
let option_field_names = fields
65+
let extendable_field_names = fields
5566
.iter()
56-
.filter(|f| f.fty == FillerType::Option)
67+
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
5768
.map(|f| f.ident.as_ref())
5869
.collect::<Vec<_>>();
5970

71+
let extendable_field_types = fields
72+
.iter()
73+
.filter(|f| matches!(f.fty, FillerType::Extendable(_)))
74+
.map(|f| f.fty.inner())
75+
.collect::<Vec<_>>();
76+
6077
let mapped_attributes = attributes
6178
.iter()
6279
.map(|a| {
@@ -84,7 +101,7 @@ impl Filler {
84101
}
85102
)*
86103
#(
87-
if !self.#vector_field_names.is_empty() {
104+
if !self.#extendable_field_names.is_empty() {
88105
return false
89106
}
90107
)*
@@ -99,7 +116,9 @@ impl Filler {
99116
impl #generics struct_patch::traits::Filler< #name #generics > for #struct_name #generics #where_clause {
100117
fn apply(&mut self, filler: #name #generics) {
101118
#(
102-
self.#vector_field_names.extend(filler.#vector_field_names.iter());
119+
if self.#extendable_field_names.is_empty() {
120+
self.#extendable_field_names.extend(filler.#extendable_field_names.into_iter());
121+
}
103122
)*
104123
#(
105124
if let Some(v) = filler.#option_field_names {
@@ -113,7 +132,7 @@ impl Filler {
113132
fn new_empty_filler() -> #name #generics {
114133
#name {
115134
#(#option_field_names: None,)*
116-
#(#vector_field_names: Vec::new(),)*
135+
#(#extendable_field_names: #extendable_field_types::default(),)*
117136
}
118137
}
119138
}
@@ -237,12 +256,7 @@ impl Field {
237256
ident, ty, attrs, ..
238257
}: syn::Field,
239258
) -> Result<Option<Field>> {
240-
let fty = if let Some(fty) = filler_type(&ty) {
241-
fty
242-
} else {
243-
return Ok(None);
244-
};
245-
259+
let mut fty = filler_type(&ty);
246260
let mut attributes = vec![];
247261

248262
for attr in attrs {
@@ -266,6 +280,10 @@ impl Field {
266280
let attribute: TokenStream = content.parse()?;
267281
attributes.push(attribute);
268282
}
283+
EXTENDABLE => {
284+
// #[filler(extendable)]
285+
fty = Some(FillerType::Extendable(extendable_filler_type(&ty)));
286+
}
269287
_ => {
270288
return Err(meta.error(format_args!(
271289
"unknown filler field attribute `{}`",
@@ -277,7 +295,7 @@ impl Field {
277295
})?;
278296
}
279297

280-
Ok(Some(Field {
298+
Ok(fty.map(|fty| Field {
281299
ident,
282300
ty,
283301
attributes,
@@ -305,13 +323,29 @@ fn filler_type(ty: &Type) -> Option<FillerType> {
305323
return Some(FillerType::Option);
306324
}
307325
}
308-
} else if segments.len() == 1 && segments[0].ident == "Vec" {
326+
} else if segments.len() == 1 && segments[0].ident == "Vec"
327+
|| segments[0].ident == "VecDeque"
328+
|| segments[0].ident == "LinkedList"
329+
|| segments[0].ident == "HashMap"
330+
|| segments[0].ident == "BTreeMap"
331+
|| segments[0].ident == "HashSet"
332+
|| segments[0].ident == "BTreeSet"
333+
|| segments[0].ident == "BinaryHeap"
334+
{
309335
if let syn::PathArguments::AngleBracketed(args) = &segments[0].arguments {
310336
if args.args.len() == 1 {
311-
return Some(FillerType::Vec);
337+
return Some(FillerType::Extendable(segments[0].ident.clone()));
312338
}
313339
}
314340
}
315341
}
316342
None
317343
}
344+
345+
fn extendable_filler_type(ty: &Type) -> Ident {
346+
if let Type::Path(type_path) = ty {
347+
type_path.path.segments[0].ident.clone()
348+
} else {
349+
panic!("#[filler(extendable)] should use on a type")
350+
}
351+
}

struct-patch/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ license.workspace = true
1111
readme.workspace = true
1212

1313
[dependencies]
14-
struct-patch-derive = { version = "=0.9.1", path = "../struct-patch-derive" }
14+
struct-patch-derive = { version = "=0.9.2", path = "../struct-patch-derive" }
1515

1616
[dev-dependencies]
1717
serde_json = "1.0"

struct-patch/examples/filler.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
1+
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
2+
use std::iter::{Extend, IntoIterator, Iterator};
13
use struct_patch::Filler;
24
#[cfg(feature = "status")]
35
use struct_patch::Status;
46

7+
// NOTE: Default, Extend, IntoIterator, is_empty are required for extendable type
8+
#[derive(Debug, Default)]
9+
struct WrapVec {
10+
inner: Vec<usize>,
11+
}
12+
13+
impl Extend<usize> for WrapVec {
14+
fn extend<WrapVec: IntoIterator<Item = usize>>(&mut self, iter: WrapVec) {
15+
self.inner.extend(iter.into_iter());
16+
}
17+
}
18+
19+
impl IntoIterator for WrapVec {
20+
type Item = usize;
21+
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
22+
fn into_iter(self) -> Self::IntoIter {
23+
Box::new(self.inner.into_iter())
24+
}
25+
}
26+
27+
impl WrapVec {
28+
pub fn is_empty(&self) -> bool {
29+
self.inner.is_empty()
30+
}
31+
}
32+
533
#[derive(Default, Filler)]
634
#[filler(attribute(derive(Debug, Default)))]
735
struct Item {
@@ -11,6 +39,15 @@ struct Item {
1139
maybe_field_int: Option<usize>,
1240
maybe_field_string: Option<String>,
1341
list: Vec<usize>,
42+
_deque: VecDeque<usize>,
43+
_linked_list: LinkedList<usize>,
44+
_map: HashMap<usize, usize>,
45+
_bmap: BTreeMap<usize, usize>,
46+
_set: HashSet<usize>,
47+
_bset: BTreeSet<usize>,
48+
_heap: BinaryHeap<usize>,
49+
#[filler(extendable)]
50+
_wrap: WrapVec,
1451
}
1552

1653
// Generated by Filler derive macro
@@ -19,6 +56,14 @@ struct Item {
1956
// struct ItemFiller {
2057
// maybe_field_int: Option<usize>,
2158
// maybe_field_string: Option<String>,
59+
// list: Vec<usize>,
60+
// _deque: VecDeque<usize>,
61+
// _linked_list: LinkedList<usize>,
62+
// _map: HashMap<usize, usize>,
63+
// _bmap: BTreeMap<usize, usize>,
64+
// _set: HashSet<usize>,
65+
// _bset: BTreeSet<usize>,
66+
// _heap: BinaryHeap<usize>,
2267
// }
2368

2469
fn main() {
@@ -36,7 +81,7 @@ fn main() {
3681

3782
assert_eq!(
3883
format!("{filler:?}"),
39-
"ItemFiller { maybe_field_int: Some(7), maybe_field_string: None, list: [] }"
84+
"ItemFiller { maybe_field_int: Some(7), maybe_field_string: None, list: [], _deque: [], _linked_list: [], _set: {}, _bset: {}, _heap: [], _wrap: WrapVec { inner: [] } }"
4085
);
4186

4287
item.apply(filler);
@@ -69,5 +114,5 @@ fn main() {
69114
let mut filler: ItemFiller = Item::new_empty_filler();
70115
filler.list = vec![3, 4];
71116
item.apply(filler);
72-
assert_eq!(item.list, vec![1, 2, 3, 4]);
117+
assert_eq!(item.list, vec![1, 2]);
73118
}

struct-patch/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,14 @@
4242
//! }
4343
//! ```
4444
//!
45-
//! More details on how to use the the derive macro, including what attributes are available, are available under [`Patch`]
45+
//! More details on how to use the the derive macro, including what attributes are available, are
46+
//! available under [`Patch`]
4647
//!
47-
//! Deriving [`Filler`] on a struct will generate a struct similar to the original one with the field with `Option`.
48+
//! Deriving [`Filler`] on a struct will generate a struct similar to the original one with the
49+
//! field with `Option`, `BTreeMap`, `BTreeSet`, `BinaryHeap`,`HashMap`, `HashSet`, `LinkedList`,
50+
//! `VecDeque `or `Vec`.
51+
//! Any struct implement `Default`, `Extend`, `IntoIterator`, `is_empty` can be used with
52+
//! `#[filler(extenable)]`.
4853
//! Unlike [`Patch`], the [`Filler`] only work on the empty fields of instance.
4954
//!
5055
//! ```rust

0 commit comments

Comments
 (0)