//! Create custom serializable changes to apply later
//! 
//! Deltastruct is an attribute macro that generates definitions for
//! serializable changes (called deltas) over structs recursively.
//! 
//! For techinical reasons, the attribute macro must be applied to a `mod`
//! definition containing a `struct` with the same name and an `impl` for
//! it's change definitions. Function names are converted to camel case to
//! appease the compiler.
//! 
//! The generated deltas are always the type name followed by `Delta`. ie: `Foo` -> `FooDelta`, `i32` -> `i32Delta`.
//! 
//! ```
//! # use deltastruct::*;
//! #[deltastruct]
//! mod Vector3 {
//!   #[derive(Clone, Copy)]
//!   pub struct Vector3 {
//!     pub x : f64,
//!     pub y : f64,
//!     pub z : f64,
//!   }
//! 
//!   impl Vector3 {
//!     fn set(&mut self, other : Vector3) {
//!       // Copy the given vec's components into our own.
//!       // Alternatively Vector3 can be Copy instead of passing a reference to
//!       // a Vector3
//!       self.x = other.x;
//!       self.y = other.y;
//!       self.z = other.z;
//!     }
//! 
//!     fn multiply_scalar(&mut self, n : f64) {
//!       // Multiply each component by the given value
//!       self.x *= n;
//!       self.y *= n;
//!       self.z *= n;
//!     }
//! 
//!     fn normalize(&mut self) {
//!       // divide each component by the vector's magnitude
//!       let magnitude = (
//!           self.x.powf(2f64) 
//!         + self.y.powf(2f64) 
//!         + self.z.powf(2f64)
//!       ).sqrt();
//!       
//!       self.x /= magnitude;
//!       self.y /= magnitude;
//!       self.z /= magnitude;
//!     }
//!   }
//! }
//! 
//! fn main() {
//!   let mut v = Vector3 {
//!     x: 3f64,
//!     y: 6f64,
//!     z: 6f64
//!   };
//! 
//! 
//!   let v_delta = Vector3Delta::MultiplyScalar(4f64);
//!   v.apply(&v_delta);
//!   v.apply(&v_delta);
//! 
//!   assert_eq!(v.x, 48f64);
//! 
//! 
//!   let v_delta = Vector3Delta::Normalize();
//!   v.apply(&v_delta);
//! 
//!   assert_eq!(v.z, 2f64/3f64);
//! 
//! 
//!   let v_delta = Vector3Delta::X(f64Delta::Set(8f64));
//!   v.apply(&v_delta);
//! 
//!   assert_eq!(v.x, 8f64);
//! }
//! 
//! ```
//! 
//! 


mod primitive_impls;
pub use primitive_impls::*;

/// An attribute macro applied to `mod`s. Will generate a Delta and it's `DeltaStruct<_>` implementation.
///
/// Applied to a `mod`. Expects the `mod` to have a `struct` with the same name
/// and an implementation. Currently at least one field is expected in the `struct`
/// definition and at least one method is expected in the implementation. This macro
/// will generate an `enum` with the same name as the mod with `Delta` appended,
/// and an implementation `impl DeltaStruct<FooDelta> for Foo`. 
/// 
/// The delta will have a tuple variant for each method defined for the struct 
/// converted to camel case, and it's tuple will be of the same type as the 
/// argument list of it's respective method (without `&mut self`). It will also
/// expose delta functions of it's fields.
/// 
/// ## Example:
/// 
/// ```
/// # use deltastruct_proc::deltastruct;
/// # use deltastruct::*;
/// #[deltastruct]
/// mod Foo {
///   struct Foo {
///     x : i32,
///     y : i32
///   }
/// 
///   impl Foo {
///     fn swap(&mut self) {
///       let tmp = self.x;
///       self.x = self.y;
///       self.y = tmp;
///     }
///     
///     fn do_stuff(&mut self, n : f64) {
///       self.x = (n * (self.x as f64)) as i32;
///     }
///   }
/// }
/// ```
/// 
/// will generate: 
/// ```
/// # use deltastruct::DeltaStruct;
/// # struct Foo;
/// # #[allow(non_camel_case_types)] struct i32Delta;
/// enum FooDelta {
///   Swap(),
///   DoStuff(f64),
///   X(i32Delta),
///   Y(i32Delta),
/// }
/// 
/// impl DeltaStruct<FooDelta> for Foo {
///   fn apply(&mut self, delta : &FooDelta) {
///     /* ... */
///   }
/// }
/// ```
pub use deltastruct_proc::deltastruct;


/// A trait derived for each tagged struct where `T` is it's generated delta.
/// 
/// ```
/// # use deltastruct::*;
/// #[deltastruct]
/// mod Foo {
///   struct Foo {
///     bar : bool
///   }
///   impl Foo {
///     fn foobar(&mut self) {}
///   }
/// }
/// ```
/// 
/// will generate:
/// ```
/// # use deltastruct::*;
/// # struct Foo;
/// # struct FooDelta;
/// impl DeltaStruct<FooDelta> for Foo { 
///   fn apply(&mut self, delta : &FooDelta) {
///     /* ... */
///   }
/// }
/// ```
pub trait DeltaStruct<T> {
  fn apply(&mut self, delta : &T);
}

#[cfg(test)]
mod tests {
  use super::*;

  #[deltastruct]
  mod Foo {
    struct Foo {
      x : i32,
      y : i32,
    }

    impl Foo {
      pub fn bar(&mut self, a : i64, b : i64) {
        self.x = ((a*b) as i32) - self.y;
      }

      pub fn baz(&mut self, data : u32) {
        self.x += data as i32;
        self.y -= data as i32;
      }
    }
  }

  #[test]
  fn self_func() {
    let mut foo = Foo{x: 4, y: 7};

    let dfoo = FooDelta::Baz(5);
    foo.apply(&dfoo);

    assert_eq!(foo.x, 9);
    assert_eq!(foo.y, 2);
    
    foo.apply(&dfoo);

    assert_eq!(foo.x, 14);
    assert_eq!(foo.y, -3);

    let dfoo = FooDelta::Bar(3,4);
    foo.apply(&dfoo);

    assert_eq!(foo.x, 15);
    assert_eq!(foo.y, -3);
  }

  #[test]
  fn field_func() {
      let mut foo = Foo{x: 3, y: 6};

      let dfoo = FooDelta::X(i32Delta::Set(1));
      foo.apply(&dfoo);

      assert_eq!(foo.x, 1);
      assert_eq!(foo.y, 6);

      let dfoo = FooDelta::Y(i32Delta::Set(-20));
      foo.apply(&dfoo);

      assert_eq!(foo.x, 1);
      assert_eq!(foo.y, -20);
  }
}
