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}