deep-struct-update 0.1.0

Struct update syntax with nesting.
Documentation
//! [Struct update syntax](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax) with support for nested structs.
//!
//! ```
//! use deep_struct_update::update;
//!
//! struct Person {
//!     name: String,
//!     job: Job,
//!     age: u8
//! }
//!
//! struct Job {
//!     position: String,
//!     company: String
//! }
//!
//! let bob = Person {
//!     name: String::from("Bob"),
//!     job: Job {
//!         position: String::from("Programmer"),
//!         company: String::from("Evil Inc.")
//!     },
//!     age: 29
//! };
//!
//! let alice = update! {
//!     name: String::from("Alice"),
//!     age: 31,
//!     job: {
//!         position: String::from("Senior Programmer")
//!     }
//!     ..bob
//! };
//!
//! assert_eq!(alice.age, 31);
//! assert_eq!(alice.name, "Alice");
//! assert_eq!(alice.job.position, "Senior Programmer");
//! assert_eq!(alice.job.company, "Evil Inc.");
//! ```

/// Update a struct value.
///
/// Can be called with a `..<expr>` at the end (like normal struct update syntax) or a `<expr>,` at the beginning.
///
/// ```
/// use deep_struct_update::update;
///
/// #[derive(Default)]
/// struct A {
///     b: B
/// }
///
/// #[derive(Default)]
/// struct B {
///     c: C
/// }
///
/// #[derive(Default)]
/// struct C {
///     num: i32
/// }
///
/// let a = update! {
///     b: { c: { num: 42 }}
///     ..A::default()
/// };
///
/// assert_eq!(a.b.c.num, 42);
/// ```
#[macro_export]
macro_rules! update {
    {$($t:tt)*} => {
        $crate::find_expr! { {} $($t)* }
    };
    {$e:expr, $($t:tt)*} => {{
        let mut temp = $e;
        $crate::update_inner! {
            @main temp, $($t)*
        }
        temp
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! find_expr {
    {{$($update:tt)*} ..$e:expr} => {{
        let mut temp = $e;
        $crate::update_inner! {
            @main temp, { $($update)* }
        }
        temp
    }};
    {{$($update:tt)*} $next:tt $($t:tt)*} => {
        $crate::find_expr! {
            {$($update)* $next} $($t)*
        }
    };
    {{$($update:tt)*}} => {
        compile_error!(concat!("Expected `..<expr>` at the end or `<expr>,` at the beginning. Got: \n\n", stringify!($($update)*), "\n\n"))
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! update_inner {
    (@main $e:expr, { $($t:tt)* }) => {
        $crate::update_inner! {
            @helper {$e} {} {} $($t)*
        }
    };
    (@main $e:expr, $value:expr $(,)?) => {
        $e = $value;
    };
    (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident:) => {
        $(
            $crate::update_inner! { @main $e.$parsed_name, $($parsed_inner)* }
        )*
        $crate::update_inner! { @main $e.$name, $($current)* }
    };
    (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident: , $other:ident $($rest:tt)*) => {
        $crate::update_inner! {
            @helper {$e} {$($parsed_name {$($parsed_inner)*})* $name {$($current)*}} {} $other $($rest)*
        }
    };
    (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident: $t:tt $($rest:tt)*) => {
        $crate::update_inner! {
            @helper {$e} {$($parsed_name {$($parsed_inner)*})* } {$($current)* $t} $name: $($rest)*
        }
    };
}