Skip to content

Commit

Permalink
Add #[patch(addable)] (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
yanganto authored Aug 19, 2024
1 parent bbfb406 commit 9fe469f
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The [examples][examples] demo following scenarios.
This crate also includes the following optional features:
- `status`(default): implements the `PatchStatus` trait for the patch struct, which provides the `is_empty` method.
- `op` (default): provide operators `<<` between instance and patch, and `+` for patches
- default: when there is a field conflict between patches, `+` will add together if the `#[patch(addable)]` is provided, else it will panic.
- `merge` (optional): implements the `Merge` trait for the patch struct, which provides the `merge` method, and `<<` between patches.
- `std`(optional):
- `box`: implements the `Patch<Box<P>>` trait for `T` where `T` implements `Patch<P>`.
Expand Down
32 changes: 28 additions & 4 deletions struct-patch-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const PATCH: &str = "patch";
const NAME: &str = "name";
const ATTRIBUTE: &str = "attribute";
const SKIP: &str = "skip";
const ADDABLE: &str = "addable";

#[proc_macro_derive(Patch, attributes(patch))]
pub fn derive_patch(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
Expand All @@ -35,6 +36,7 @@ struct Field {
ty: Type,
attributes: Vec<TokenStream>,
retyped: bool,
addable: bool,
}

impl Patch {
Expand Down Expand Up @@ -123,6 +125,23 @@ impl Patch {
#[cfg(not(feature = "merge"))]
let patch_merge_impl = quote!();

#[cfg(feature = "op")]
let addable_handles = fields
.iter()
.map(|f| {
if f.addable {
quote!(
Some(a + b)
)
} else {
// TODO handle #[patch(add=fn)] fields
quote!(
panic!("There are conflict patches, please use `#[patch(addable)]` if you want to add these values.")
)
}
})
.collect::<Vec<_>>();

#[cfg(feature = "op")]
let op_impl = quote! {
impl #generics core::ops::Shl<#name #generics> for #struct_name #generics #where_clause {
Expand Down Expand Up @@ -151,8 +170,7 @@ impl Patch {
#(
#renamed_field_names: match (self.#renamed_field_names, rhs.#renamed_field_names) {
(Some(a), Some(b)) => {
// TODO handle #[patch(add=)] fields
panic!("There are conflict patches on {}.{}", stringify!(#name), stringify!(#renamed_field_names))
#addable_handles
},
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
Expand All @@ -162,8 +180,7 @@ impl Patch {
#(
#original_field_names: match (self.#original_field_names, rhs.#original_field_names) {
(Some(a), Some(b)) => {
// TODO handle #[patch(add=)] fields
panic!("There are conflict patches on {}.{}", stringify!(#name), stringify!(#original_field_names))
#addable_handles
},
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
Expand Down Expand Up @@ -373,6 +390,7 @@ impl Field {
let mut attributes = vec![];
let mut field_type = None;
let mut skip = false;
let mut addable = false;

for attr in attrs {
if attr.path().to_string().as_str() != PATCH {
Expand Down Expand Up @@ -404,6 +422,10 @@ impl Field {
let expr: LitStr = meta.value()?.parse()?;
field_type = Some(expr.parse()?)
}
ADDABLE => {
// #[patch(addable)]
addable = true;
}
_ => {
return Err(meta.error(format_args!(
"unknown patch field attribute `{}`",
Expand All @@ -423,6 +445,7 @@ impl Field {
retyped: field_type.is_some(),
ty: field_type.unwrap_or(ty),
attributes,
addable,
}))
}
}
Expand Down Expand Up @@ -500,6 +523,7 @@ mod tests {
.unwrap(),
attributes: vec![],
retyped: true,
addable: false,
}],
};
let result = Patch::from_ast(syn::parse2(input).unwrap()).unwrap();
Expand Down
11 changes: 5 additions & 6 deletions struct-patch/examples/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use struct_patch::Patch;
#[patch(attribute(derive(Clone, Debug, Default)))]
struct Item {
field_complete: bool,
#[patch(addable)]
field_int: usize,
field_string: String,
}
Expand Down Expand Up @@ -45,12 +46,10 @@ fn main() {
field_string: None,
};

// let final_item_from_merge = item.clone() << (conflict_patch.clone() + the_other_patch.clone());
// Will get panic `There are conflict patches on ItemPatch.field_int`
//
// TODO
// Will be handdled as the discussion
// https://github.com/yanganto/struct-patch/pull/32#issuecomment-2283154990
// NOTE: The values of #[patch(addable)] can be added together.
let final_item_from_conflict =
item.clone() << (conflict_patch.clone() + the_other_patch.clone());
assert_eq!(final_item_from_conflict.field_int, 3);

let final_item_without_bracket =
item.clone() << conflict_patch.clone() << the_other_patch.clone();
Expand Down

0 comments on commit 9fe469f

Please sign in to comment.