type_lib/lib.rs
1//! # type-lib
2//!
3//! Parse-dont-validate domain types for Rust, with zero-overhead wrappers.
4//!
5//! `type-lib` turns runtime invariants into compile-time guarantees. Rather than
6//! re-checking a value everywhere it is used, you check it **once**, at
7//! construction, and carry a type that can only exist in a valid state. Functions
8//! that accept such a type are freed from defensive validation: the type system
9//! has already done it.
10//!
11//! ## The foundation
12//!
13//! Two pieces compose to express "a value that is known to be valid":
14//!
15//! - [`Validator`] — a reusable, type-level validation rule. It is implemented on
16//! a zero-sized marker type and selected through the type system, so it carries
17//! no state and adds no storage.
18//! - [`Refined`] — a `#[repr(transparent)]` wrapper holding a value proven to
19//! satisfy a [`Validator`]. It has the same size and layout as the value it
20//! wraps, so the guarantee is free at runtime.
21//!
22//! A ready-made [`ValidationError`] covers rules that need only a code and a
23//! message; rules that need structured failures define their own error type via
24//! [`Validator::Error`].
25//!
26//! ## Built-in rules and combinators
27//!
28//! You rarely need to hand-write a rule. The [`rules`] module ships the common
29//! ones — length ([`NonEmpty`](rules::NonEmpty), [`MaxLen`](rules::MaxLen),
30//! [`LenRange`](rules::LenRange), …), numeric ([`Positive`](rules::Positive),
31//! [`InRange`](rules::InRange), …), and string content
32//! ([`Ascii`](rules::Ascii), [`Alphanumeric`](rules::Alphanumeric),
33//! [`Trimmed`](rules::Trimmed)). The [`combinator`] module composes them at the
34//! type level with [`And`](combinator::And), [`Or`](combinator::Or), and
35//! [`Not`](combinator::Not).
36//!
37//! ## Example
38//!
39//! ```rust
40//! use type_lib::combinator::And;
41//! use type_lib::rules::{LenRange, Trimmed};
42//! use type_lib::Refined;
43//!
44//! // A username: 3–16 characters with no surrounding whitespace.
45//! type Username<'a> = Refined<&'a str, And<Trimmed, LenRange<3, 16>>>;
46//!
47//! let user = Username::new("alice");
48//! assert!(user.is_ok());
49//! assert!(Username::new(" x ").is_err()); // whitespace + too short
50//! ```
51//!
52//! Writing a bespoke rule is just as direct when the built-ins do not fit:
53//!
54//! ```rust
55//! use type_lib::{Refined, ValidationError, Validator};
56//!
57//! struct Even;
58//!
59//! impl Validator<i64> for Even {
60//! type Error = ValidationError;
61//!
62//! fn validate(value: &i64) -> Result<(), Self::Error> {
63//! if value % 2 == 0 {
64//! Ok(())
65//! } else {
66//! Err(ValidationError::new("even", "value must be even"))
67//! }
68//! }
69//! }
70//!
71//! type EvenI64 = Refined<i64, Even>;
72//! assert!(EvenI64::new(4).is_ok());
73//! assert!(EvenI64::new(5).is_err());
74//! ```
75//!
76//! ## Deriving validated newtypes
77//!
78//! With the `derive` feature, `#[derive(Validated)]` generates a named domain
79//! type from a one-field tuple struct, enforcing a [`Validator`] at construction:
80//!
81//! ```rust
82//! # #[cfg(feature = "derive")] {
83//! use type_lib::rules::InRange;
84//! use type_lib::Validated;
85//!
86//! #[derive(Validated)]
87//! #[valid(InRange<0, 100>)]
88//! pub struct Percent(i32);
89//!
90//! assert!(Percent::new(50).is_ok());
91//! assert!(Percent::new(150).is_err());
92//! # }
93//! ```
94//!
95//! ## Cargo features
96//!
97//! - `std` *(default)* — implies `alloc` and implements `std::error::Error` for
98//! [`ValidationError`].
99//! - `alloc` — enables the length rules for owned `String` / `Vec<T>` values.
100//! - `derive` — enables the `Validated` derive macro (see [Deriving validated
101//! newtypes](#deriving-validated-newtypes)).
102//!
103//! With no features (`default-features = false`), the crate is `no_std` and the
104//! core [`Validator`] / [`Refined`] API plus all borrowed-value rules are
105//! available unchanged.
106//!
107//! ## Stability
108//!
109//! `v1.0.0` is the stable API. The public surface is frozen under SemVer: no
110//! breaking change ships without a `2.0`. New rules, combinators, and trait impls
111//! arrive as additive minor releases. The error codes returned by the built-in
112//! rules are stable across `1.x`.
113//!
114//! # License
115//!
116//! Dual-licensed under Apache-2.0 OR MIT.
117
118#![doc(html_root_url = "https://docs.rs/type-lib")]
119#![cfg_attr(docsrs, feature(doc_cfg))]
120#![cfg_attr(not(feature = "std"), no_std)]
121#![deny(missing_docs)]
122#![deny(unsafe_op_in_unsafe_fn)]
123#![deny(unused_must_use)]
124#![deny(unused_results)]
125#![deny(clippy::unwrap_used)]
126#![deny(clippy::expect_used)]
127#![deny(clippy::todo)]
128#![deny(clippy::unimplemented)]
129#![deny(clippy::print_stdout)]
130#![deny(clippy::print_stderr)]
131#![deny(clippy::dbg_macro)]
132#![deny(clippy::unreachable)]
133#![deny(clippy::undocumented_unsafe_blocks)]
134#![deny(clippy::missing_safety_doc)]
135
136#[cfg(feature = "alloc")]
137extern crate alloc;
138
139mod error;
140mod refined;
141mod validator;
142
143pub mod combinator;
144pub mod prelude;
145pub mod rules;
146
147pub use crate::error::ValidationError;
148pub use crate::refined::Refined;
149pub use crate::validator::Validator;
150
151/// Derives a validated newtype: a one-field tuple struct gains a checked
152/// constructor, accessors, and `Deref`, enforcing a [`Validator`] at construction.
153///
154/// Available with the `derive` feature. Annotate the struct with
155/// `#[valid(<Validator>)]`, where the validator is any rule implementing
156/// [`Validator`] for the field type — a [built-in rule](crate::rules), a
157/// [combinator], or your own.
158///
159/// The generated `new` returns `Result<Self, <V as Validator<T>>::Error>`; `get`,
160/// `into_inner`, `Deref`, and `AsRef` expose the inner value. The field stays
161/// private, so construction must go through `new`.
162///
163/// # Examples
164///
165/// ```rust
166/// use type_lib::combinator::And;
167/// use type_lib::rules::{LenRange, Trimmed};
168/// use type_lib::Validated;
169///
170/// #[derive(Validated)]
171/// #[valid(And<Trimmed, LenRange<3, 16>>)]
172/// pub struct Username(String);
173///
174/// let user = Username::new("alice".to_owned()).expect("valid");
175/// assert_eq!(user.get(), "alice");
176/// assert!(Username::new(" ".to_owned()).is_err());
177/// ```
178#[cfg(feature = "derive")]
179#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
180pub use type_lib_derive::Validated;
181
182/// Crate version string, populated by Cargo at build time.
183///
184/// Useful for diagnostics, startup banners, and tests that need to assert the
185/// crate metadata seen by Cargo.
186///
187/// # Examples
188///
189/// Compare the exported value with Cargo's package version:
190///
191/// ```rust
192/// assert_eq!(type_lib::VERSION, env!("CARGO_PKG_VERSION"));
193/// ```
194///
195/// Embed the version in generated output:
196///
197/// ```rust
198/// let banner = format!("type-lib {}", type_lib::VERSION);
199/// assert!(banner.starts_with("type-lib "));
200/// ```
201pub const VERSION: &str = env!("CARGO_PKG_VERSION");