deep_struct_update/lib.rs
1//! [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.
2//!
3//! ```
4//! use deep_struct_update::update;
5//!
6//! struct Person {
7//! name: String,
8//! job: Job,
9//! age: u8
10//! }
11//!
12//! struct Job {
13//! position: String,
14//! company: String
15//! }
16//!
17//! let bob = Person {
18//! name: String::from("Bob"),
19//! job: Job {
20//! position: String::from("Programmer"),
21//! company: String::from("Evil Inc.")
22//! },
23//! age: 29
24//! };
25//!
26//! let alice = update! {
27//! name: String::from("Alice"),
28//! age: 31,
29//! job: {
30//! position: String::from("Senior Programmer")
31//! }
32//! ..bob
33//! };
34//!
35//! assert_eq!(alice.age, 31);
36//! assert_eq!(alice.name, "Alice");
37//! assert_eq!(alice.job.position, "Senior Programmer");
38//! assert_eq!(alice.job.company, "Evil Inc.");
39//! ```
40
41/// Update a struct value.
42///
43/// Can be called with a `..<expr>` at the end (like normal struct update syntax) or a `<expr>,` at the beginning.
44///
45/// ```
46/// use deep_struct_update::update;
47///
48/// #[derive(Default)]
49/// struct A {
50/// b: B
51/// }
52///
53/// #[derive(Default)]
54/// struct B {
55/// c: C
56/// }
57///
58/// #[derive(Default)]
59/// struct C {
60/// num: i32
61/// }
62///
63/// let a = update! {
64/// b: { c: { num: 42 }}
65/// ..A::default()
66/// };
67///
68/// assert_eq!(a.b.c.num, 42);
69/// ```
70#[macro_export]
71macro_rules! update {
72 {$($t:tt)*} => {
73 $crate::find_expr! { {} $($t)* }
74 };
75 {$e:expr, $($t:tt)*} => {{
76 let mut temp = $e;
77 $crate::update_inner! {
78 @main temp, $($t)*
79 }
80 temp
81 }};
82}
83
84#[doc(hidden)]
85#[macro_export]
86macro_rules! find_expr {
87 {{$($update:tt)*} ..$e:expr} => {{
88 let mut temp = $e;
89 $crate::update_inner! {
90 @main temp, { $($update)* }
91 }
92 temp
93 }};
94 {{$($update:tt)*} $next:tt $($t:tt)*} => {
95 $crate::find_expr! {
96 {$($update)* $next} $($t)*
97 }
98 };
99 {{$($update:tt)*}} => {
100 compile_error!(concat!("Expected `..<expr>` at the end or `<expr>,` at the beginning. Got: \n\n", stringify!($($update)*), "\n\n"))
101 };
102}
103
104#[doc(hidden)]
105#[macro_export]
106macro_rules! update_inner {
107 (@main $e:expr, { $($t:tt)* }) => {
108 $crate::update_inner! {
109 @helper {$e} {} {} $($t)*
110 }
111 };
112 (@main $e:expr, $value:expr $(,)?) => {
113 $e = $value;
114 };
115 (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident:) => {
116 $(
117 $crate::update_inner! { @main $e.$parsed_name, $($parsed_inner)* }
118 )*
119 $crate::update_inner! { @main $e.$name, $($current)* }
120 };
121 (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident: , $other:ident $($rest:tt)*) => {
122 $crate::update_inner! {
123 @helper {$e} {$($parsed_name {$($parsed_inner)*})* $name {$($current)*}} {} $other $($rest)*
124 }
125 };
126 (@helper {$e:expr} {$($parsed_name:ident {$($parsed_inner:tt)*})*} {$($current:tt)*} $name:ident: $t:tt $($rest:tt)*) => {
127 $crate::update_inner! {
128 @helper {$e} {$($parsed_name {$($parsed_inner)*})* } {$($current)* $t} $name: $($rest)*
129 }
130 };
131}