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
//! This crate provides a way to define type wrappers (guards) that behave as close as
//! possible to the underlying type, but guarantee to uphold arbitrary invariants at all times.
//!
//! The name comes from latin _praesidio_, which means _guard_.
//!
//! # Basic usage
//!
//! The simplest way to create a [guard](Guard) is to use the [`define!`](prae_macro::define) macro.
//!
//! Let's create a `Text` type. It will be a wrapper around a `String` with an invariant that the
//! value is not empty:
//!
//! ```
//! use prae::{define, Guard};
//!
//! // Here we define our new type.
//! // `pub` - the visibility of our type;
//! // `Text` - the name of our type;
//! // `String` - the underlying type;
//! // `ensure $closure` - the closure that returns `true` if the value is valid.
//! define!(pub Text: String ensure |t| !t.is_empty());
//!
//! // We can easily create a value of our type. Note that the type of the agrument
//! // is not `String`. That is because `Text::new(...)` accepts anything
//! // that is `Into<String>`.
//! let mut t = Text::new("not empty!").unwrap();
//! assert_eq!(t.get(), "not empty!");
//!
//! // One way to mutate the value is to call the `mutate` method.
//! // See docs for `prae::Guard` to learn about other methods.
//! t.mutate(|t| *t = format!("{} updated", t));
//! assert_eq!(t.get(), "not empty! updated");
//!
//! // Creating an invalid value is not possible.
//! assert!(Text::new("").is_err());
//! ```
//!
//! Okay, we're getting there. Right now our type will accept every non-empty string and reject any
//! zero-length string. But does it really protect us from a possible abuse? The following example
//! shows us, that the answer is no:
//!
//! ```
//! # use prae::{define, Guard};
//! # define!(pub Text: String ensure |t| !t.is_empty());
//! // Technically not empty, but makes no sense as a "text" for a human.
//! assert!(Text::new(" \n\n ").is_ok());
//!
//! // Trailing whitespace should not be allowed either.
//! assert!(Text::new(" text ").is_ok());
//! ```
//!
//! One way to solve this is to tell the user of our type to always trim the string before any
//! construction/mutation of the `Text`. But this can go wrong very easily. It would be better if
//! we could somehow embed this behaviour into our type. And we can!
//!
//! ```
//! use prae::{define, Guard};
//!
//! define! {
//! /// Btw, this comment will document our type!
//! pub Text: String
//! // We will mutate given value before every construction/mutation.
//! adjust |t| {
//! let trimmed = t.trim();
//! if trimmed.len() != t.len() {
//! *t = trimmed.to_string()
//! }
//! }
//! ensure |t| !t.is_empty()
//! }
//!
//! // This won't work anymore.
//! assert!(Text::new(" ").is_err());
//!
//! // And this will be automatically adjusted.
//! let t = Text::new(" no trailing whitespace anymore! ").unwrap();
//! assert_eq!(t.get(), "no trailing whitespace anymore!");
//! ```
//!
//! That's a lot better!
//!
//! # Custom errors
//!
//! Both [`ConstructionError`](ConstructionError) and [`MutationError`](MutationError) are
//! useful wrappers around some inner error. They provide access to the values that were in play when
//! the error occured. By default (when you use `ensure` closure inside the
//! [`define!`]), the inner error is just `&'static str` with the default error message.
//!
//! Sometimes, however, we might want to use our own type. In this case, we should use `validate`
//! instead of `ensure`:
//!
//! ```rust
//! use prae::{define, Guard};
//!
//! #[derive(Debug)]
//! pub enum Error {
//! Empty,
//! NotEnoughWords,
//! TooManyWords,
//! }
//!
//! define! {
//! /// A text that contains two words.
//! pub TwoWordText: String
//! adjust |t| *t = t.trim().to_owned()
//! validate |t| -> Result<(), Error> {
//! let wc = t.split_whitespace().count();
//! if t.is_empty() {
//! Err(Error::Empty)
//! } else if wc < 2 {
//! Err(Error::NotEnoughWords)
//! } else if wc > 2 {
//! Err(Error::TooManyWords)
//! } else {
//! Ok(())
//! }
//! }
//! }
//!
//! assert!(matches!(TwoWordText::new(" ").unwrap_err().inner, Error::Empty));
//! assert!(matches!(TwoWordText::new("word").unwrap_err().inner, Error::NotEnoughWords));
//! assert!(matches!(TwoWordText::new("word word word").unwrap_err().inner, Error::TooManyWords));
//! assert!(TwoWordText::new("word word").is_ok());
//! ```
//!
//! # Extending our types
//!
//! If you want to reuse adjustment/validation behaviour of some type in a new type, you should use
//! [`extend!`]. It's just like [`define!`], but it's inner type should be [`Guard`].
//!
//! ```
//! use prae::{define, extend, Guard};
//!
//! #[derive(Debug)]
//! pub enum TextError {
//! Empty,
//! }
//!
//! define! {
//! /// A non-empty string without trailing whitespace.
//! pub Text: String
//! adjust |t| *t = t.trim().to_owned()
//! validate |t| -> Result<(), TextError> {
//! if t.is_empty() {
//! Err(TextError::Empty)
//! } else {
//! Ok(())
//! }
//! }
//! }
//!
//! #[derive(Debug)]
//! pub enum TwoWordTextError {
//! Empty,
//! NotEnoughWords,
//! TooManyWords,
//! }
//!
//! impl From<TextError> for TwoWordTextError {
//! fn from(te: TextError) -> Self {
//! match te {
//! TextError::Empty => Self::Empty,
//! }
//! }
//! }
//!
//! extend! {
//! /// A text that contains two words.
//! pub TwoWordText: Text
//! validate |t| -> Result<(), TwoWordTextError> {
//! // We don't need to check if `t` is empty, since
//! // it already passed the validation of `Text` at
//! // this point.
//! let wc = t.split_whitespace().count();
//! if wc < 2 {
//! Err(TwoWordTextError::NotEnoughWords)
//! } else if wc > 2 {
//! Err(TwoWordTextError::TooManyWords)
//! } else {
//! Ok(())
//! }
//! }
//! }
//!
//! assert!(matches!(TwoWordText::new(" ").unwrap_err().inner, TwoWordTextError::Empty));
//! assert!(matches!(TwoWordText::new("word").unwrap_err().inner, TwoWordTextError::NotEnoughWords));
//! assert!(matches!(TwoWordText::new("word word word").unwrap_err().inner, TwoWordTextError::TooManyWords));
//! assert!(TwoWordText::new("word word").is_ok());
//! ```
//!
//! # Integration with serde
//!
//! If you enable `serde` feature, every [`Guard`] will automatically implement
//! [`Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) and
//! [`Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) if its inner type implements them.
//! Deserialization will automatically return an error if the data is not invalid.
//!
//! Here is an example of some API implemented using [`axum`](https://crates.io/crates/axum):
//!
//! ```ignore
//! use prae::{define, Guard};
//! use axum::{extract, handler::post, Router};
//!
//! define! {
//! Text: String
//! adjust |t| *t = t.trim().to_owned()
//! validate |t| !t.is_empty()
//! }
//!
//! async fn save_text(extract::Json(text): extract::Json<Text>) {
//! // Our `text` was automatically validated. We don't need to
//! // do anything manually at all!
//! // ...
//! }
//!
//! let app = Router::new().route("/texts", post(save_text));
//! ```
//!
//! # Optimising for performance
//!
//! If you find yourself in a situation where always adjusting/validating values of your type
//! is a performance issue, you can opt in to avoid those extra calls using methods under the
//! `unchecked` feature gate. Those methods (`new_unchecked`, `mutate_unchecked`, etc.) don't
//! adjust/validate your values at all, making __you__ responsible for the validity of your data.
//!
//! [`Guard`]: Guard
//! [`define!`]: prae_macro::define
//! [`extend!`]: prae_macro::extend
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unsafe_code)]
#![warn(unused_crate_dependencies)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
mod core;
pub use crate::core::*;
pub use prae_macro::{define, extend};
// We need this to silince the unused_crate_dependencies warning.
// See: https://github.com/rust-lang/rust/issues/57274
#[cfg(test)]
mod test_deps {
use assert_matches as _;
use serde as _;
use serde_json as _;
}