struct-patch 0.11.0

A library that helps you implement partial updates for your structs.
Documentation
#![cfg(feature = "option")]

#[cfg(feature = "merge")]
use crate::Merge;
use crate::Patch;

/// Patch implementation for Option<T>
/// This implementation is used to apply a patch to an optional field
/// The default behavior when patching on `None`, will use the `From` trait to convert the patch to
/// the struct type.
/// Else,
/// The feature `none_as_default`, will patch on default instance when patching on `None`.
/// The feature `keep_none` will keep none when patching on none.
#[cfg(all(not(feature = "keep_none"), not(feature = "none_as_default")))]
impl<T, P> Patch<Option<P>> for Option<T>
where
    T: Patch<P> + From<P>,
{
    fn apply(&mut self, patch: Option<P>) {
        if let Some(patch) = patch {
            if let Some(self_) = self {
                self_.apply(patch);
            } else {
                *self = Some(patch.into());
            }
        } else {
            *self = None;
        }
    }

    fn into_patch(self) -> Option<P> {
        self.map(|x| x.into_patch())
    }

    fn into_patch_by_diff(self, previous_struct: Self) -> Option<P> {
        match (self, previous_struct) {
            (Some(self_), Some(previous_struct_)) => {
                Some(self_.into_patch_by_diff(previous_struct_))
            }
            (Some(self_), None) => Some(self_.into_patch()),
            (None, _) => None,
        }
    }

    fn new_empty_patch() -> Option<P> {
        Some(T::new_empty_patch())
    }
}

#[cfg(feature = "keep_none")]
impl<T, P> Patch<Option<P>> for Option<T>
where
    T: Patch<P>,
{
    fn apply(&mut self, patch: Option<P>) {
        if let Some(patch) = patch {
            if let Some(self_) = self {
                self_.apply(patch);
                return;
            }
        }
        *self = None;
    }

    fn into_patch(self) -> Option<P> {
        self.map(|x| x.into_patch())
    }

    fn into_patch_by_diff(self, previous_struct: Self) -> Option<P> {
        match (self, previous_struct) {
            (Some(self_), Some(previous_struct_)) => {
                Some(self_.into_patch_by_diff(previous_struct_))
            }
            (Some(self_), None) => Some(self_.into_patch()),
            (None, _) => None,
        }
    }

    fn new_empty_patch() -> Option<P> {
        Some(T::new_empty_patch())
    }
}
#[cfg(feature = "none_as_default")]
impl<T, P> Patch<Option<P>> for Option<T>
where
    T: Patch<P> + Default,
{
    fn apply(&mut self, patch: Option<P>) {
        if let Some(patch) = patch {
            if let Some(self_) = self {
                self_.apply(patch);
            } else {
                let mut instance = T::default();
                instance.apply(patch);
                *self = Some(instance);
            }
        } else {
            *self = None;
        }
    }

    fn into_patch(self) -> Option<P> {
        self.map(|x| x.into_patch())
    }

    fn into_patch_by_diff(self, previous_struct: Self) -> Option<P> {
        match (self, previous_struct) {
            (Some(self_), Some(previous_struct_)) => {
                Some(self_.into_patch_by_diff(previous_struct_))
            }
            (Some(self_), None) => Some(self_.into_patch()),
            (None, _) => None,
        }
    }

    fn new_empty_patch() -> Option<P> {
        Some(T::new_empty_patch())
    }
}

#[cfg(feature = "merge")]
impl<T> Merge for Option<T>
where
    T: Merge,
{
    fn merge(self, other: Self) -> Self {
        if let Some(other) = other {
            let mut self_ = self;
            if let Some(self_) = self_.take() {
                Some(self_.merge(other))
            } else {
                Some(other)
            }
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    extern crate alloc;
    use crate as struct_patch;
    use crate::Patch;
    use alloc::string::String;

    #[test]
    fn test_patch_option() {
        #[derive(Patch, Debug, PartialEq)]
        struct Item {
            field: u32,
            other: String,
        }

        impl From<ItemPatch> for Item {
            fn from(patch: ItemPatch) -> Self {
                Item {
                    field: patch.field.unwrap_or_default(),
                    other: patch.other.unwrap_or_default(),
                }
            }
        }

        let mut item = Some(Item {
            field: 1,
            other: String::from("hello"),
        });
        let patch = Some(ItemPatch {
            field: None,
            other: Some(String::from("bye")),
        });

        item.apply(patch);
        assert_eq!(
            item,
            Some(Item {
                field: 1,
                other: String::from("bye")
            })
        );
    }

    /// Tests for nested optional fields
    /// See https://stackoverflow.com/questions/44331037/how-can-i-distinguish-between-a-deserialized-field-that-is-missing-and-one-that
    /// and https://github.com/serde-rs/serde/issues/1042
    /// To understand how to manage optional fields in patch with serde
    mod nested {
        use super::*;
        use serde::Deserialize;
        use serde::Deserializer;

        #[derive(PartialEq, Debug, Patch, Deserialize)]
        #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
        struct B {
            c: u32,
            d: u32,
        }

        #[derive(PartialEq, Debug, Patch, Deserialize)]
        #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
        struct A {
            #[patch(
                name = "Option<BPatch>",
                attribute(serde(deserialize_with = "deserialize_optional_field", default))
            )]
            b: Option<B>,
        }

        impl From<BPatch> for B {
            fn from(patch: BPatch) -> Self {
                B {
                    c: patch.c.unwrap_or_default(),
                    d: patch.d.unwrap_or_default(),
                }
            }
        }

        fn deserialize_optional_field<'de, T, D>(
            deserializer: D,
        ) -> Result<Option<Option<T>>, D::Error>
        where
            D: Deserializer<'de>,
            T: Deserialize<'de>,
        {
            Ok(Some(Option::deserialize(deserializer)?))
        }

        #[test]
        fn test_optional_nested_present() {
            let mut a = A {
                b: Some(B { c: 0, d: 0 }),
            };
            let data = r#"{ "b": { "c": 1 } }"#;
            let patch: APatch = serde_json::from_str(data).unwrap();
            assert_eq!(
                patch,
                APatch {
                    b: Some(Some(BPatch {
                        c: Some(1),
                        d: None
                    }))
                }
            );
            a.apply(patch);
            assert_eq!(
                a,
                A {
                    b: Some(B { c: 1, d: 0 })
                }
            );
        }

        #[test]
        fn test_optional_nested_absent() {
            let mut a = A {
                b: Some(B { c: 0, d: 0 }),
            };
            let data = r#"{ }"#;
            let patch: APatch = serde_json::from_str(data).unwrap();
            assert_eq!(patch, APatch { b: None });
            a.apply(patch);
            assert_eq!(
                a,
                A {
                    b: Some(B { c: 0, d: 0 })
                }
            );
        }

        #[test]
        fn test_optional_nested_null() {
            let mut a = A {
                b: Some(B { c: 0, d: 0 }),
            };
            let data = r#"{ "b": null }"#;
            let patch: APatch = serde_json::from_str(data).unwrap();
            assert_eq!(patch, APatch { b: Some(None) });
            a.apply(patch);
            assert_eq!(a, A { b: None });
        }
    }
}