seventy/lib.rs
1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2
3//! Seventy is a simple [newtype](https://doc.rust-lang.org/rust-by-example/generics/new_types.html) sanitizer and validator.
4//!
5//! The [`macro@seventy`] procedural macro is provided to automatically
6//! implement sanitization, validation, and other logic.
7//!
8//! # Sanitizing
9//!
10//! Sanitization mutates a target. Sanitization is run before validation.
11//!
12//! ## Examples
13//!
14//! The example below sanitizes the inner string by trimming surrounding
15//! whitespace.
16//!
17//! ```
18//! use seventy::{builtins::string::*, seventy, Newtype};
19//!
20//! #[seventy(sanitize(trim))]
21//! pub struct Username(String);
22//!
23//! assert_eq!(
24//! Username::try_new(" username ").unwrap().into_inner(),
25//! "username"
26//! );
27//! ```
28//!
29//! ## Built-in Sanitizers
30//!
31//! Seventy provides [`built-in sanitizers`] for common use cases.
32//!
33//! ## Custom Sanitizers
34//!
35//! Custom sanitizers can be defined with the [`Sanitizer`] trait.
36//!
37//! The sanitizer below divides the target by the given value.
38//!
39//! ```
40//! use std::ops::DivAssign;
41//!
42//! use seventy::{core::Sanitizer, seventy, Newtype};
43//!
44//! #[allow(non_snake_case)]
45//! pub struct divide_by<T>(pub T);
46//!
47//! impl<T> Sanitizer<T> for divide_by<T>
48//! where
49//! T: DivAssign<T> + Copy,
50//! {
51//! fn sanitize(&self, target: &mut T) {
52//! target.div_assign(self.0);
53//! }
54//! }
55//!
56//! #[seventy(sanitize(divide_by(5.0)))]
57//! pub struct DivideBy(f32);
58//!
59//! assert_eq!(DivideBy::try_new(10.0).unwrap().into_inner(), 2.0);
60//! ```
61//!
62//! # Validating
63//!
64//! Validation checks if a target adheres to a set of rules. Validation is run
65//! after sanitization.
66//!
67//! ## Examples
68//!
69//! The example below validates the inner string is alphanumeric.
70//!
71//! ```
72//! use seventy::{builtins::string::*, seventy, Newtype};
73//!
74//! #[seventy(validate(alphanumeric))]
75//! pub struct Username(String);
76//!
77//! assert!(Username::try_new("username").is_ok());
78//! assert!(Username::try_new("u$ername").is_err());
79//! ```
80//!
81//! ## Built-in Validators
82//!
83//! Seventy provides [`built-in validators`] for common use cases.
84//!
85//! ## Custom Validators
86//!
87//! Custom validators can be defined with the [`Validator`] trait.
88//!
89//! The validator below checks if the target is even.
90//!
91//! ```
92//! use seventy::{core::Validator, seventy, Newtype};
93//!
94//! #[allow(non_snake_case)]
95//! pub struct even_i64;
96//!
97//! impl Validator<i64> for even_i64 {
98//! fn validate(&self, target: &i64) -> bool {
99//! target.abs() % 2 == 0
100//! }
101//! }
102//!
103//! #[seventy(validate(even_i64))]
104//! pub struct EvenI64(i64);
105//!
106//! assert!(EvenI64::try_new(2).is_ok());
107//! assert!(EvenI64::try_new(3).is_err());
108//! ```
109//!
110//! ## Errors?
111//!
112//! Seventy does not support error handling. A validator only returns a boolean
113//! indicating whether the validation result is valid or invalid. If you need to
114//! know the specific reason why a newtype couldn't be created, Seventy is not
115//! the crate for you.
116//!
117//! # Forwarding
118//!
119//! Some sanitizers and validators can be nested inside each other! The outer
120//! sanitizer or validator will then forward some value to the inner.
121//!
122//! The [`length`] validator below forwards the character length of the string
123//! to the inner [`gt`] validator, which will validate that the length is
124//! greater than 5.
125//!
126//! ```
127//! use seventy::{
128//! builtins::{compare::*, string::*},
129//! seventy, Newtype,
130//! };
131//!
132//! #[seventy(validate(length::chars(gt(5))))]
133//! pub struct Username(String);
134//!
135//! assert!(Username::try_new("username").is_ok());
136//! assert!(Username::try_new("user").is_err());
137//! ```
138//!
139//! # Upgrading
140//!
141//! Upgrades automatically implement useful functionality. More about upgrades
142//! and the different types of upgrades can be found in the documentation
143//! for the [`macro@seventy`] procedural macro.
144//!
145//! ## Examples
146//!
147//! The example below uses the `deref` upgrade, which implements `Deref` on
148//! the newtype.
149//!
150//! ```
151//! use seventy::{builtins::string::*, seventy, Newtype};
152//!
153//! #[seventy(upgrades(deref))]
154//! pub struct Username(String);
155//!
156//! let username = Username::try_new("username").unwrap();
157//! assert_eq!(*username, "username");
158//! ```
159//!
160//! # Incorporating it all
161//!
162//! ## Examples
163//!
164//! The example below first trims the target and then validates if the trimmed
165//! target is alphanumeric.
166//!
167//! ```
168//! use seventy::{builtins::string::*, seventy, Newtype};
169//!
170//! #[seventy(upgrades(deref), sanitize(trim), validate(alphanumeric))]
171//! pub struct Username(String);
172//!
173//! let username = Username::try_new(" username ").unwrap();
174//! assert_eq!(*username, "username");
175//!
176//! assert!(Username::try_new(" u$ername ").is_err());
177//! ```
178//!
179//! [`built-in sanitizers`]: seventy::builtins
180//! [`Sanitizer`]: seventy::core::Sanitizer
181//! [`built-in validators`]: seventy::builtins
182//! [`Validator`]: seventy::core::Validator
183//! [`length`]: seventy::builtins::string::length
184//! [`gt`]: seventy::builtins::compare::gt
185
186extern crate self as seventy;
187
188// Re-export the procedural macro.
189pub use seventy_macros::seventy;
190
191// Re-export the newtype trait.
192pub use core::Newtype;
193
194pub mod builtins;
195pub mod core;