Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [

[workspace.package]
authors = ["Antonio Yang <yanganto@gmail.com>"]
version = "0.9.2"
version = "0.9.3"
edition = "2021"
categories = ["development-tools"]
keywords = ["struct", "patch", "macro", "derive", "overlay"]
Expand Down
83 changes: 77 additions & 6 deletions struct-patch-derive/src/filler.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use std::str::FromStr;
use syn::{parenthesized, DeriveInput, LitStr, Result, Type};
use syn::meta::ParseNestedMeta;
use syn::spanned::Spanned;
use syn::{parenthesized, DeriveInput, Error, Lit, LitStr, Result, Type};

const FILLER: &str = "filler";
const ATTRIBUTE: &str = "attribute";
const EXTENDABLE: &str = "extendable";
const EMPTY_VALUE: &str = "empty_value";

pub(crate) struct Filler {
visibility: syn::Visibility,
Expand All @@ -16,18 +19,27 @@ pub(crate) struct Filler {
fields: Vec<Field>,
}

#[derive(Debug, PartialEq)]
enum FillerType {
Option,
/// The type with `Default`, `Extend`, `IntoIterator` and `is_empty` implementations
Extendable(Ident),
/// The type with a value defined for empty
NativeValue(Lit),
}

impl FillerType {
fn inner(&self) -> &Ident {
if let FillerType::Extendable(ident) = self {
ident
} else {
panic!("FillerType::Option has no inner indent")
panic!("Only FillerType::Extendable has inner indent")
}
}
fn value(&self) -> &Lit {
if let FillerType::NativeValue(lit) = self {
lit
} else {
panic!("Only FillerType::NativeValue has value")
}
}
}
Expand Down Expand Up @@ -58,7 +70,7 @@ impl Filler {

let option_field_names = fields
.iter()
.filter(|f| f.fty == FillerType::Option)
.filter(|f| matches!(f.fty, FillerType::Option))
.map(|f| f.ident.as_ref())
.collect::<Vec<_>>();

Expand All @@ -74,6 +86,18 @@ impl Filler {
.map(|f| f.fty.inner())
.collect::<Vec<_>>();

let native_value_field_names = fields
.iter()
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
.map(|f| f.ident.as_ref())
.collect::<Vec<_>>();

let native_value_field_values = fields
.iter()
.filter(|f| matches!(f.fty, FillerType::NativeValue(_)))
.map(|f| f.fty.value())
.collect::<Vec<_>>();

let mapped_attributes = attributes
.iter()
.map(|a| {
Expand Down Expand Up @@ -105,6 +129,11 @@ impl Filler {
return false
}
)*
#(
if self.#native_value_field_names != #native_value_field_values {
return false
}
)*
true
}
}
Expand All @@ -115,6 +144,11 @@ impl Filler {
let filler_impl = quote! {
impl #generics struct_patch::traits::Filler< #name #generics > for #struct_name #generics #where_clause {
fn apply(&mut self, filler: #name #generics) {
#(
if self.#native_value_field_names == #native_value_field_values {
self.#native_value_field_names = filler.#native_value_field_names;
}
)*
#(
if self.#extendable_field_names.is_empty() {
self.#extendable_field_names.extend(filler.#extendable_field_names.into_iter());
Expand All @@ -133,6 +167,7 @@ impl Filler {
#name {
#(#option_field_names: None,)*
#(#extendable_field_names: #extendable_field_types::default(),)*
#(#native_value_field_names: #native_value_field_values,)*
}
}
}
Expand Down Expand Up @@ -282,7 +317,24 @@ impl Field {
}
EXTENDABLE => {
// #[filler(extendable)]
fty = Some(FillerType::Extendable(extendable_filler_type(&ty)));
if fty.is_some() {
return Err(meta
.error("The field is already the field of filler, we can't defined more than once"));
}
fty = Some(FillerType::Extendable(none_option_filler_type(&ty)));
}
EMPTY_VALUE => {
// #[filler(empty_value=some value)]
if fty.is_some() {
return Err(meta
.error("The field is already the field of filler, we can't defined more than once"));
}
if let Some(lit) = get_lit(path, &meta)? {
fty = Some(FillerType::NativeValue(lit));
} else {
return Err(meta
.error("empty_value needs a clear value to define empty"));
}
}
_ => {
return Err(meta.error(format_args!(
Expand Down Expand Up @@ -342,10 +394,29 @@ fn filler_type(ty: &Type) -> Option<FillerType> {
None
}

fn extendable_filler_type(ty: &Type) -> Ident {
fn none_option_filler_type(ty: &Type) -> Ident {
if let Type::Path(type_path) = ty {
type_path.path.segments[0].ident.clone()
} else {
panic!("#[filler(extendable)] should use on a type")
}
}

fn get_lit(attr_name: String, meta: &ParseNestedMeta) -> syn::Result<Option<syn::Lit>> {
let expr: syn::Expr = meta.value()?.parse()?;
let mut value = &expr;
while let syn::Expr::Group(e) = value {
value = &e.expr;
}
if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = value {
Ok(Some(lit.clone()))
} else {
Err(Error::new(
expr.span(),
format!(
"expected serde {} attribute to be lit: `{} = \"...\"`",
attr_name, attr_name
),
))
}
}
2 changes: 1 addition & 1 deletion struct-patch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license.workspace = true
readme.workspace = true

[dependencies]
struct-patch-derive = { version = "=0.9.2", path = "../struct-patch-derive" }
struct-patch-derive = { version = "=0.9.3", path = "../struct-patch-derive" }

[dev-dependencies]
serde_json = "1.0"
Expand Down
14 changes: 13 additions & 1 deletion struct-patch/examples/filler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ impl WrapVec {
#[filler(attribute(derive(Debug, Default)))]
struct Item {
field_complete: bool,
// Will check the field is equal to the value to define the field is empty or not
#[filler(empty_value = 0)]
field_int: usize,
field_string: String,
maybe_field_int: Option<usize>,
Expand Down Expand Up @@ -81,7 +83,7 @@ fn main() {

assert_eq!(
format!("{filler:?}"),
"ItemFiller { maybe_field_int: Some(7), maybe_field_string: None, list: [], _deque: [], _linked_list: [], _set: {}, _bset: {}, _heap: [], _wrap: WrapVec { inner: [] } }"
"ItemFiller { field_int: 0, maybe_field_int: Some(7), maybe_field_string: None, list: [], _deque: [], _linked_list: [], _set: {}, _bset: {}, _heap: [], _wrap: WrapVec { inner: [] } }"
);

item.apply(filler);
Expand Down Expand Up @@ -115,4 +117,14 @@ fn main() {
filler.list = vec![3, 4];
item.apply(filler);
assert_eq!(item.list, vec![1, 2]);

let mut filler: ItemFiller = Item::new_empty_filler();
filler.field_int = 7;
item.apply(filler);
assert_eq!(item.field_int, 7);

let mut filler: ItemFiller = Item::new_empty_filler();
filler.field_int = 5;
item.apply(filler);
assert_eq!(item.field_int, 7);
}