changeset/lib.rs
1//! Library to generate a changeset.
2//!
3//! # Usage
4//!
5//! Add dependency to Cargo.toml:
6//!
7//! ```toml
8//! [dependencies]
9//! changeset = "0.1"
10//! ```
11//!
12//! And in your main.rs or lib.rs:
13//!
14//! ```ignore
15//! #[macro_use]
16//! extern crate changeset;
17//! ```
18//!
19//! # Exemple
20//!
21//! ```ignore
22//! changeset!(UserChangeSet {
23//! /// User's name
24//! name: String,
25//! age: usize
26//! });
27//! ```
28//!
29//! This will generate:
30//!
31//! ```
32//! struct UserChangeSet {
33//! /// User's name
34//! pub name: Option<String>,
35//! pub age: Option<usize>,
36//! }
37//!
38//! impl UserChangeSet {
39//! /// Some doc here
40//! pub fn new() -> UserChangeSet {
41//! UserChangeSet {
42//! name: None,
43//! age: None,
44//! }
45//! }
46//!
47//! /// User's name
48//! pub fn name(mut self, name: String) -> UserChangeSet {
49//! self.name = Some(name);
50//! self
51//! }
52//!
53//! pub fn age(mut self, age: usize) -> UserChangeSet {
54//! self.age = Some(age);
55//! self
56//! }
57//!
58//! /// Some doc here
59//! pub fn merge(&mut self, rhs: UserChangeSet) {
60//! if let Some(name) = rhs.name {
61//! self.name = Some(name);
62//! }
63//! if let Some(age) = rhs.age {
64//! self.age = Some(age);
65//! }
66//! }
67//!
68//! // I may add some new functions later
69//! }
70//! ```
71//!
72//! You can also generate public struct just by adding `pub` keyword.
73
74#![cfg_attr(not(feature = "std"), no_std)]
75
76#[macro_export]
77macro_rules! changeset {
78 (
79 $( #[$attr:meta] )*
80 pub $name:ident {
81 $(
82 $( #[$attrf:meta] )*
83 $field:ident : $type:ty
84 ),*
85 }
86 ) => {
87 __changeset!(
88 $(#[$attr])*
89 (pub) $name {
90 $(
91 $(#[$attrf])*
92 $field: $type
93 ),*
94 }
95 );
96 };
97
98 (
99 $( #[$attr:meta] )*
100 $name:ident {
101 $(
102 $( #[$attrf:meta] )*
103 $field:ident : $type:ty
104 ),*
105 }
106 ) => {
107 __changeset!(
108 $(#[$attr])*
109 () $name {
110 $(
111 $(#[$attrf])*
112 $field: $type
113 ),*
114 }
115 );
116 };
117}
118
119#[macro_export]
120#[doc(hidden)]
121macro_rules! __changeset {
122 (
123 $( #[$attr:meta] )*
124 ($($vis:tt)*) $name:ident {
125 $(
126 $( #[$attrf:meta] )*
127 $field:ident : $type:ty
128 ),*
129 }
130 ) => {
131 $(#[$attr])*
132 $($vis)* struct $name {
133 $(
134 $(#[$attrf])*
135 pub $field : Option<$type>,
136 )*
137 }
138
139 impl $name {
140 /// Create a new changeset.
141 pub fn new() -> $name {
142 $name {
143 $(
144 $field: None,
145 )*
146 }
147 }
148
149 $(
150 $(#[$attrf])*
151 pub fn $field<T: Into<$type>>(mut self, $field: T) -> $name {
152 self.$field = Some($field.into());
153 self
154 }
155 )*
156
157 /// Merge with another changeset.
158 pub fn merge(&mut self, rhs: $name) {
159 $(
160 if let Some($field) = rhs.$field {
161 self.$field = Some($field);
162 }
163 )*
164 }
165
166 /// Check if there is a value that has changed.
167 pub fn has_changed(&self) -> bool {
168 $(
169 if let Some(_) = self.$field {
170 return true;
171 }
172 )*
173 false
174 }
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 #[allow(dead_code)]
182 mod in_mod {
183 changeset!(pub PubStruct {
184 name: String
185 });
186 }
187
188 changeset!(PrivStruct {
189 name: String
190 });
191
192 #[test]
193 fn access_pub() {
194 let s = in_mod::PubStruct::new();
195 assert_eq!(s.name, None);
196 }
197
198 #[test]
199 fn merge() {
200 let mut a = PrivStruct::new().name("test".to_owned());
201 let b = PrivStruct::new().name("success".to_owned());
202 assert_eq!(a.name, Some("test".to_owned()));
203 a.merge(b);
204 assert_eq!(a.name, Some("success".to_owned()));
205 }
206
207 #[test]
208 fn has_changed() {
209 let mut a = PrivStruct::new();
210 assert_eq!(a.has_changed(), false);
211 a = a.name("success".to_owned());
212 assert_eq!(a.has_changed(), true);
213 }
214}