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