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.
101//!
102//! With no features (`default-features = false`), the crate is `no_std` and the
103//! core [`Validator`] / [`Refined`] API plus all borrowed-value rules are
104//! available unchanged.
105//!
106//! ## Stability
107//!
108//! The public API established in `v0.2.0` is the surface 1.0 will preserve;
109//! `v0.5.0` added the rule and combinator sets and `v0.6.0` the `derive` macro,
110//! all additively.
111//!
112//! # License
113//!
114//! Dual-licensed under Apache-2.0 OR MIT.
115
116#![doc(html_root_url = "https://docs.rs/type-lib")]
117#![cfg_attr(docsrs, feature(doc_cfg))]
118#![cfg_attr(not(feature = "std"), no_std)]
119#![deny(missing_docs)]
120#![deny(unsafe_op_in_unsafe_fn)]
121#![deny(unused_must_use)]
122#![deny(unused_results)]
123#![deny(clippy::unwrap_used)]
124#![deny(clippy::expect_used)]
125#![deny(clippy::todo)]
126#![deny(clippy::unimplemented)]
127#![deny(clippy::print_stdout)]
128#![deny(clippy::print_stderr)]
129#![deny(clippy::dbg_macro)]
130#![deny(clippy::unreachable)]
131#![deny(clippy::undocumented_unsafe_blocks)]
132#![deny(clippy::missing_safety_doc)]
133
134#[cfg(feature = "alloc")]
135extern crate alloc;
136
137mod error;
138mod refined;
139mod validator;
140
141pub mod combinator;
142pub mod prelude;
143pub mod rules;
144
145pub use crate::error::ValidationError;
146pub use crate::refined::Refined;
147pub use crate::validator::Validator;
148
149/// Derives a validated newtype: a one-field tuple struct gains a checked
150/// constructor, accessors, and `Deref`, enforcing a [`Validator`] at construction.
151///
152/// Available with the `derive` feature. Annotate the struct with
153/// `#[valid(<Validator>)]`, where the validator is any rule implementing
154/// [`Validator`] for the field type — a [built-in rule](crate::rules), a
155/// [combinator], or your own.
156///
157/// The generated `new` returns `Result<Self, <V as Validator<T>>::Error>`; `get`,
158/// `into_inner`, `Deref`, and `AsRef` expose the inner value. The field stays
159/// private, so construction must go through `new`.
160///
161/// # Examples
162///
163/// ```rust
164/// use type_lib::combinator::And;
165/// use type_lib::rules::{LenRange, Trimmed};
166/// use type_lib::Validated;
167///
168/// #[derive(Validated)]
169/// #[valid(And<Trimmed, LenRange<3, 16>>)]
170/// pub struct Username(String);
171///
172/// let user = Username::new("alice".to_owned()).expect("valid");
173/// assert_eq!(user.get(), "alice");
174/// assert!(Username::new(" ".to_owned()).is_err());
175/// ```
176#[cfg(feature = "derive")]
177#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
178pub use type_lib_derive::Validated;
179
180/// Crate version string, populated by Cargo at build time.
181///
182/// Useful for diagnostics, startup banners, and tests that need to assert the
183/// crate metadata seen by Cargo.
184///
185/// # Examples
186///
187/// Compare the exported value with Cargo's package version:
188///
189/// ```rust
190/// assert_eq!(type_lib::VERSION, env!("CARGO_PKG_VERSION"));
191/// ```
192///
193/// Embed the version in generated output:
194///
195/// ```rust
196/// let banner = format!("type-lib {}", type_lib::VERSION);
197/// assert!(banner.starts_with("type-lib "));
198/// ```
199pub const VERSION: &str = env!("CARGO_PKG_VERSION");