1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
#![cfg_attr(docsrs, feature(doc_cfg))]

//! # What is `prae`?
//!
//! This crate aims to provide a better way to define types that require
//! validation. `prae` **is not** a validation library, but a library that
//! **helps developers** to define validation-requiring types with **very little
//! effort**.
//!
//! # How it works?
//!
//! The main way to use `prae` is through [`define!`](crate::define) macro.
//!
//! For example, suppose you want to create a `Username` type. You want this
//! type to be a string, and you don't want it to be empty. Traditionally, would
//! create a wrapper struct with getter and setter functions, like this
//! simplified example:
//! ```
//! #[derive(Debug)]
//! pub struct Username(String);
//!
//! impl Username {
//!     pub fn new(username: &str) -> Result<Self, &'static str> {
//!         let username = username.trim().to_owned();
//!         if username.is_empty() {
//!             Err("value is invalid")
//!         } else {
//!             Ok(Self(username))
//!         }
//!     }
//!
//!     pub fn get(&self) -> &str {
//!         &self.0
//!     }
//!
//!     pub fn set(&mut self, username: &str) -> Result<(), &'static str> {
//!         let username = username.trim().to_owned();
//!         if username.is_empty() {
//!             Err("value is invalid")
//!         } else {
//!             self.0 = username;
//!             Ok(())
//!         }
//!    }
//! }
//!
//! let username = Username::new(" my username ").unwrap();
//! assert_eq!(username.get(), "my username");
//!
//! let err = Username::new("  ").unwrap_err();
//! assert_eq!(err, "value is invalid");
//! ```
//!
//! Using `prae`, you will do it like this:
//! ```
//! use prae::define;
//!
//! define! {
//!     pub Username: String
//!     adjust |username| *username = username.trim().to_owned()
//!     ensure |username| !username.is_empty()
//! }
//!
//! let username = Username::new(" my username ").unwrap();
//! assert_eq!(username.get(), "my username");
//!
//! let err = Username::new("  ").unwrap_err();
//! assert_eq!(err.inner, "value is invalid");
//! assert_eq!(err.value, "");
//! ```
//!
//! Futhermore, `prae` allows you to use custom errors and extend your types.
//! See docs for [`define!`](crate::define) for more information and examples.
//!
//! # Additional features
//!
//! ## `*_unprocessed` functions
//!
//! By default, all methods of the wrappers generated by
//! [`define!`](crate::define) (which are just aliases of the
//! [`Bounded`](crate::Bounded) type) will run the adjustment/validation (if
//! present) routines on every construction and mutation.
//!
//! If you find yourself in a situation where you know for sure that some
//! construction/mutation is valid, you can opt out of this using
//! `*_unprocessed` functions (e.g. `foo.set_unprocessed(value)` instead of
//! `foo.set(value)`) and save a bit of computations.
//!
//! To be able to use these functions, just enable the `unprocessed`
//! feature of the crate.
//!
//! ## Serde integration
//!
//! You can enable serde integration with the `serde` feature. It will implement
//! [`Serialize`](serde::Serialize) and [`Deserialize`](serde::Deserialize)
//! traits for the wrappers if their inner type implements them. The
//! deserialization will automatically fail if it contains invalid value. Here
//! is an example:
//!
//! ```
//! use serde::{Deserialize, Serialize};
//! use prae::define;
//!
//! define! {
//!     Username: String
//!     adjust   |username| *username = username.trim().to_string()
//!     validate(&'static str) |username| {
//!         if username.is_empty() {
//!             Err("username is empty")
//!         } else {
//!             Ok(())
//!         }
//!     }
//! }
//!
//! #[derive(Debug, Deserialize, Serialize)]
//! struct User {
//!     username: Username,
//! }
//!
//! // Serialization works as expected.
//! let u = User {
//!     username: Username::new("  john doe  ").unwrap(),
//! };
//! let j = serde_json::to_string(&u).unwrap();
//! assert_eq!(j, r#"{"username":"john doe"}"#);
//!
//! // Deserialization with invalid data fails.
//! let e = serde_json::from_str::<User>(r#"{ "username": "  " }"#).unwrap_err();
//! assert_eq!(e.to_string(), "username is empty at line 1 column 20");
//!
//! // And here we get a nice adjusted value.
//! let u = serde_json::from_str::<User>(r#"{ "username": "  john doe  " }"#).unwrap();
//! assert_eq!(u.username.get(), "john doe");
//! ```
//!
//! # Drawbacks
//!
//! Although proc macros are very powerful, they aren't free. In this case, you
//! have to pull up additional dependencies such as [`syn`](https://docs.rs/syn) and
//! [`quote`](https://docs.rs/quote), and expect a slightly slower compile times.

mod core;

pub use crate::core::*;

/// A macro that makes defining new types easy.
///
/// This macro uses custom syntax described below and introduces two new types
/// to the scope: the [bounded](crate::Bounded) type (that you will use in your
/// code, so check it's documentation of available methods) and it's associated
/// [bound](crate::Bound) (which will hidden from your docs).
///
/// # Macro structure
///
/// ## Type signature
///
/// This part describes the visibility of the type, it's name and it's inner
/// type:
/// ```
/// use prae::define;
///
/// define! {
///     pub UserId: u64
///     // ...
/// }
///
/// define! {
///     UserName: String
///     // ...
/// }
/// ```
///
/// ## Type extension
///
/// In case you want to extend the validation/adjustment logic of some other
/// type, you should just specify the other type's [`Bound`](crate::Bound):
/// ```
/// # use prae::define;
/// # define! {
/// #      pub UserId: u64
/// #      // ...
/// #  }
/// define! {
///     pub AdminUserID: u64
///     extend UserIdBound
///     // ...
/// }
/// ```
/// When you do this, your type will execute adjustment and validation (see
/// below) of the extended type first, then run the adjustment and validation of
/// the current type.
///
/// Note that such extension is only possible when the types have the same inner
/// type.
///
/// ## Adjustment closure
///
/// This part describes how a value of your type should be mutated before
/// every construction or mutation.
/// ```
/// # use prae::define;
/// define! {
///     pub UserName: String
///     // ..
///     adjust |name: &mut String| *name = name.trim().to_owned()
///     // ...
/// }
/// ```
///
/// ## Validation closure
///
/// This part describes how a value of your type should be validated before
/// every construction or mutation. There are two ways you can do this: using
/// `ensure` closure or using `validate` closure.
///
/// ### Using `ensure`
///
/// This is the simplest method. In this case, your closure returns `true` if
/// the value is valid and `false` otherwise. The downside of this method is
/// that you can't use custom error types. If the value is invalid, the error
/// will be just a string `"value is invalid"`.
/// ```
/// # use prae::define;
/// define! {
///     pub UserName: String
///     // ...
///     ensure |name: &String| !name.is_empty()
/// }
/// ```
///
/// ### Using `validate`
///
/// This method allows you to use custom errors.
/// ```
/// # use prae::define;
/// #[derive(Debug)]
/// pub enum UserNameError {
///     Empty,
/// }
///
/// define! {
///     pub UserName: String
///     // ...
///     validate(UserNameError) |name: &String| {
///         if name.is_empty() {
///             Err(UserNameError::Empty)
///         } else {
///             Ok(())
///         }
///     }
/// }
/// ```
///
/// # Examples
///
/// ## Basic usage
///
/// Suppose we want to implement some types for a typography app. Let's start
/// from a `Text` type, which represents a string with some symbols
/// (non-empty!).
/// ```
/// # use prae::define;
/// /// A string with some symbols.
/// define! {
///     pub Text: String
///     ensure |text| !text.is_empty()
/// }
///
/// // Let's try it.
/// let text1 = Text::new("some text").unwrap();
/// assert_eq!(text1.get(), "some text");
///
/// // Now with an invalid value.
/// let err = Text::new("").unwrap_err();
/// assert_eq!(err.inner, "value is invalid");
/// assert_eq!(err.value, "");
///
/// // But what if...
/// assert!(Text::new("   ").is_ok());
/// ```
///
/// The last line doesn't look right. Although the string `"   "` contains
/// symbols, it's all whitespace. How can we solve that? Well, we could
/// use `!text.trim().is_empty()` inside the `ensure` closure. But what if we
/// give give it a string with some symbols and trailing whitespace, like `"
/// some text\n"`? This string will pass validation and will be saved in it's
/// original form. We don't want that in this particular case. Let's fix it with
/// `adjust` closure!
///
/// ```
/// # use prae::define;
/// /// A non-empty string without trailing whitespace.
/// define! {
///     pub Text: String
///     adjust |text| *text = text.trim().to_owned()
///     ensure |text| !text.is_empty()
/// }
///
/// // Now we can't use a string with only whitespace.
/// let err = Text::new("  ").unwrap_err();
/// assert_eq!(err.inner, "value is invalid");
/// assert_eq!(err.value, "");
///
/// // And the trailing whitespace will be truncated.
/// let text = Text::new("  some text\n").unwrap();
/// assert_eq!(text.get(), "some text");
/// ```
///
/// ## Custom errors
///
/// Okay, so far so good. The only problem is: right now our `Text` type will
/// return string error `"value is invalid"` if it's value is not valid. If we
/// want to make our error more descriptive, we should create a custom error!
/// ```
/// # use prae::define;
/// /// Error that can happen during construction/mutation of `Text`.
/// #[derive(Debug)]
/// pub enum TextError {
///     Empty,
/// }
///
/// define! {
///     pub Text: String
///     adjust |text| *text = text.trim().to_owned()
///     validate(TextError) |text| {
///         if text.is_empty() {
///             Err(TextError::Empty)
///         } else {
///             Ok(())
///         }
///     }
/// }
///
/// // Let's try it!
/// let err = Text::new("  ").unwrap_err();
/// assert!(matches!(err.inner, TextError::Empty));
/// assert_eq!(err.value, "");
/// ```
///
/// ## Type extension
///
/// It's a perfect time to introduce a new type: `CapitalizedText`. This type is
/// very similar to `Text`, but requires the first letter to be capitalized.
/// Sinces this type inherits invariants of `Text`, we will just extend `Text`
/// type to make the code less verbose.
/// ```
/// # use prae::define;
/// /// Error that can happen during construction/mutation of `Text`.
/// #[derive(Debug)]
/// pub enum TextError {
///     Empty,
/// }
///
/// define! {
///     pub Text: String
///     adjust |text| *text = text.trim().to_owned()
///     validate(TextError) |text| {
///         if text.is_empty() {
///             Err(TextError::Empty)
///         } else {
///             Ok(())
///         }
///     }
/// }
///
/// /// Error that can happen during construction/mutation of `CapitalizedText`.
/// #[derive(Debug)]
/// pub enum CapitalizedTextError {
///     Empty,
///     NotCapitalized,
/// }
///
/// impl From<TextError> for CapitalizedTextError {
///     fn from(err: TextError) -> Self {
///         match err {
///             TextError::Empty => Self::Empty,
///         }
///     }
/// }
///
/// define! {
///     pub CapitalizedText: String
///     extend TextBound
///     validate(CapitalizedTextError) |text| {
///         if text.chars().next().unwrap().is_lowercase() {
///             Err(CapitalizedTextError::NotCapitalized)
///         } else {
///             Ok(())
///         }
///     }
/// }
///
/// // Let's try it.
/// let text = CapitalizedText::new("    Some text ").unwrap();
/// assert_eq!(text.get(), "Some text");
///
/// let err = CapitalizedText::new("  some other text").unwrap_err();
/// assert!(matches!(err.inner, CapitalizedTextError::NotCapitalized));
/// assert_eq!(err.value, "some other text");
///
/// let err = CapitalizedText::new("   ").unwrap_err();
/// assert!(matches!(err.inner, CapitalizedTextError::Empty));
/// assert_eq!(err.value, "");
/// ```
pub use prae_macro::define;

#[cfg(test)]
mod tests {}