derive-patch 0.0.1

Macro to generate a struct with all field Optional (`partial!`) and another macro to generate a patch (`patch!`).
Documentation
/*
 * Copyright (2020) by Marcel Lambert
 * This project is licensed under the MIT Open Source license.
 * For more information, see the LICENSE file in this repository.
 */

//! This crate contains 2 macros.
//!
//! One for generating a `partial` Variant of an object. (Inspired by typescript ```Partial<Object>```)

#![deny(missing_docs)]

use proc_macro::TokenStream;

///
///
///
#[proc_macro_derive(HelloWorld)]
pub fn partial(input: TokenStream) -> TokenStream {
    input
}

#[cfg(test)]
mod tests {

    //#[derive(Partial)]
    struct Example {
        foo: f64,
        bar: Option<String>,

        // #[ignorePartial(default => 42)]
        // without an default, we cannot provide a `build`. unless the ignored value type is 'default'
        something_special: u32,

        // #[forcePartial(default => 5)] // we cannot provide a Default trait for the partial, unless default provided, or value type is Default
        id: u32,
    }

    #[derive(PartialEq, Default)]
    // Default: only when nothing forced or the forced values have a default or are default buildable
    // derives can be customized by user
    struct PartialExample {
        foo: Option<f64>,
        bar: Option<Option<String>>,
        id: u32, //forced, so not optional
    }

    impl PartialExample {
        //only when we have a forced value
        fn new(id: u32) -> PartialExample {
            PartialExample {
                id,
                //alternatively use ```.. Default::default()``` if present
                foo: None,
                bar: None,
            }
        }

        fn complete(&self) -> bool {
            //for each attribute
            self.foo.is_some() && self.bar.is_some()
        }

        fn apply(&self, obj: &mut Example) {
            //for each attribute
            if let Some(foo) = &self.foo {
                obj.foo = *foo;
            }
            if let Some(bar) = &self.bar {
                // clone because 'Option' does not implement Copy
                obj.bar = bar.clone();
            }
        }

        /// add all attributes set here in to the given Partial
        ///
        /// If an attribute in here is already set in `obj`, then it gets overwritten
        fn merge_into(&self, obj: &mut PartialExample) {
            //for each attribute
            if let Some(foo) = &self.foo {
                obj.foo = Some(*foo);
            }
            if let Some(bar) = &self.bar {
                obj.bar = Some(bar.clone());
            }
        }

        /// check all attributes that are present on BOTH objects, if they are equal.
        /// If there are no attributes present in both, will return true.
        fn partial_equal_existing(&self, obj: &PartialExample) -> bool {
            //for each attribute
            if let Some(foo) = &self.foo {
                if let Some(otherfoo) = &obj.foo {
                    if (*foo - *otherfoo).abs() > std::f64::EPSILON {
                        // special comparison for f32 and f64
                        return false;
                    }
                }
            }

            if let Some(bar) = &self.bar {
                if let Some(otherbar) = &obj.bar {
                    if *bar != *otherbar {
                        return false;
                    }
                }
            }

            true
        }

        /// Counts how many attributes are set
        ///
        /// Forced values are not counted, as they are always present
        fn count(&self) -> u32 {
            let mut count = 0;
            //for each attribute
            if self.foo.is_some() {
                count += 1;
            }

            if self.bar.is_some() {
                count += 1;
            }

            count
        }

        fn build(&self) -> Result<Example, ()> {
            if let Some(foo) = &self.foo {
                if let Some(bar) = &self.bar {
                    return Ok(Example {
                        foo: *foo,
                        bar: bar.clone(),
                        id: self.id,
                        something_special: 42,
                    });
                }
            }
            Err(())
        }
    }

    impl PartialEq<Example> for PartialExample {
        fn eq(&self, other: &Example) -> bool {
            if let Some(foo) = &self.foo {
                if other.foo != *foo {
                    return false;
                }
            }

            if let Some(bar) = &self.bar {
                if other.bar != *bar {
                    return false;
                }
            }

            true
        }
    }

    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}