microtype/
lib.rs

1#![warn(clippy::all)]
2#![warn(rustdoc::all)]
3#![no_std]
4
5//! A library to generate "microtypes" (A.K.A. "newtypes"). Opinionated in favour of ergonomics
6//! over maximum flexibility.
7//!
8//! A microtype is a thin wrapper around an underlying type, that helps disambiguate similar uses
9//! of the same type
10//!
11//! For example, consider the following code from an imaginary e-commerce web backend:
12//! ```
13//! # #[macro_use]
14//! # extern crate microtype;
15//! # fn retrieve_user_id() -> String { "".into() }
16//! # fn retrieve_order_id() -> String { "".into() }
17//! fn handle_order(user_id: String, order_id: String) {
18//!    // ...
19//! }
20//!
21//! fn main() {
22//!   let user_id = retrieve_user_id();
23//!   let order_id = retrieve_order_id();
24//!
25//!   handle_order(order_id, user_id);
26//! }
27//! ```
28//!
29//! This code compiles, but has a bug: the `order_id` and `user_id` are used in the wrong order.
30//! This example is fairly trivial and easy to spot, but the larger a project gets, the harder it
31//! becomes to detect these issues. This becomes especially troublesome if you want to refactor. For
32//! example, if you wanted to swap the order of the arguments, you'd have to make sure you visited
33//! all the calls to this function and swapped the arguments manually. Luckily, we can get the
34//! compiler to help with this.
35//!
36//! Microtypes solve this problem. They wrap some inner type, and allow the compiler to distinguish
37//! between different uses of the same underlying type. For example, we could rewrite the earlier
38//! example as:
39//!
40//! ```compile_fail
41//! # #[macro_use]
42//! # extern crate microtype;
43//! # fn retrieve_user_id() -> String { "".into() }
44//! # fn retrieve_order_id() -> String { "".into() }
45//! // Generate wrappers around String called UserId and OrderId
46//! microtype! {
47//!     String {
48//!         UserId,
49//!         OrderId,
50//!     }
51//! }
52//!
53//! fn handle_order(user_id: UserId, order_id: OrderId) {
54//!     // ...
55//! }
56//!
57//! fn main() {
58//!     let user_id: OrderId = retrieve_user_id();
59//!     let order_id: UserId = retrieve_order_id();
60//!
61//!     handle_order(order_id, user_id);  // Error: incompatible types
62//! }
63//! ```
64//! Excellent, a run-time error has been turned into a compile time error.
65//!
66//! ## Basic usage
67//!
68//! To declare microtypes, use the ['microtype::microtype'] macro:
69//! ```
70//! # #[macro_use]
71//! # extern crate microtype;
72//! # use microtype::Microtype;
73//! microtype! {
74//!     // these attributes apply to all microtypes defined in this block
75//!     #[derive(Debug, Clone)]
76//!     String {
77//!         #[derive(PartialEq)]
78//!         UserId,    // implements Debug, Clone and PartialEq
79//!
80//!         Username,  // only implements Debug and Clone
81//!     }
82//!
83//!     // multiple inner types can be used in a single macro
84//!     i64 {
85//!         Timestamp
86//!     }
87//!
88//!     // use the `#[secret]` attribute to mark a type as "secret"
89//!     #[secret]
90//!     String {
91//!         Password
92//!     }
93//!
94//!     // use `#[secret(serialize)]` to make a secret type implement serde::Serialize
95//!     String {
96//!         SessionToken
97//!     }
98//! }
99//!
100//! fn main() {
101//!     let user_id = UserId::new("id".into());  // create new UserId
102//!     let string = user_id.into_inner();       // consume UserId, return inner String
103//!     let username = Username::new(string);    // create new Username
104//!
105//!     // sometimes you need to explicitly change the type of a value:
106//!     let user_id: UserId = username.convert();
107//!
108//!     // microtypes also optionally implement Deref
109//!     let length = user_id.len();
110//!     assert_eq!(length, 2);
111//! }
112//! ```
113//!
114//!
115//! ## Secrets
116//!
117//! Some types may be considered "sensitive" (for example: passwords, session tokens, etc).
118//! For this purpose, microtypes can be marked as `#[secret]`:
119//! ```
120//! # use microtype::microtype;
121//! # use microtype::SecretMicrotype;
122//! # use microtype::secrecy::ExposeSecret;
123//! microtype! {
124//!   #[secret]
125//!   String {
126//!     Password
127//!   }
128//! }
129//!
130//! fn main() {
131//!     let password = Password::new("secret password".to_string());
132//!     assert_eq!(password.expose_secret(), "secret password");
133//! }
134//! ```
135//! Secret microtypes don't implement [`Microtype`], instead they implement
136//! [`SecretMicrotype`], which has a much more restrictive API:
137//!  - Mutable and owned access to the inner data is not possible, it is only possible to get a
138//!  shared reference to the inner data via [`secrecy::ExposeSecret::expose_secret`], which makes
139//!  accesses easier to audit.
140//!  - They `#[derive(Debug, Clone)]` (and optionally `Serialize` and `Deserialize`) but do not support adding extra derive macros.
141//!
142//! Internally, they wrap the contained data in [`secrecy::Secret`], which provides some nice
143//! safety features. In particular:
144//!  - The debug representation is redacted. This is can prevent against accidentally leaking
145//!  data to logs, but it still *has* a `Debug` implementation (so you can still
146//!  `#[derive(Debug)]` on structs which contain secret data)
147//!  - Data is zeroized after use, meaning the underlying data is overwritten with 0s, which
148//!  ensures sensitive data exists in memory only for as long as is needed. (Caveat: not all types
149//!  have perfect zeroize implementations. Notably `Vec` (and `String`) will not be able to zeroize
150//!  previous allocations)
151//!  - when using `serde`, secret microtypes do not implement `Serialize`, to avoid accidentally
152//!  leaking secret data
153//!
154//! ## Serializable Secrets
155//!
156//! The fact that secret microtypes do not implement `Serialize` can be overly restrictive
157//! sometimes. There are many types (e.g. session tokens) which are sensitive enough to warrant
158//! redacting their debug implementation, but also need to be serialized. For types like this, you
159//! can use `#[secret(serialize)]` to make the type implement `Serialize`.
160//!
161//! ```ignore
162//! # use serde::Serialize;
163//! # use microtype::microtype;
164//! microtype! {
165//!     #[secret(serialize)]
166//!     String {
167//!         SessionToken
168//!     }
169//! }
170//!
171//! #[derive(Serialize)]
172//! struct LoginResponse {
173//!     token: SessionToken
174//! }
175//! ```
176//!
177//! ## Type Hints
178//!
179//! Proc-macros are run before type information is available, so can only use the text of the
180//! invocation. Given that, a proc-macro can't distinguish between the `String` type provided by
181//! the standard library and a custom `struct String;`. You can use a type hint to mark a microtype
182//! as wrapping a well-known type, to generate more helpful implementations for you:
183//!
184//! - If the wrapped type is a `String`, you can use `#[string]` to provide a few extra
185//! implementations (e.g. `FromStr`, `From<&str>`, `Display`)
186//! - If the wrapped type is an integer type, you can use `#[int]` to provide other extra
187//! implementations: various `fmt` traits (e.g. `UpperHex`, etc), as well as arithmetic traits
188//! (`Add`, `AddAssign`, etc). These are incomplete, please open a PR/issue if there are implementations
189//! you rely on that are missing
190//!
191//! For example:
192//! ```
193//! # use microtype::*;
194//! microtype! {
195//!   #[string]
196//!   String {
197//!     Email
198//!   }
199//!
200//!   #[int]
201//!   i32 {
202//!     Num
203//!   }
204//! }
205//!
206//! fn main() {
207//!   let email = Email::from("email");
208//!   let num = Num::from(123);
209//!
210//!   println!("{email}");  
211//!   println!("display: {num}, hex: {num:x}");
212//! }
213//! ``` 
214//!
215//!
216//! ## Feature flags
217//!
218//! The following feature flags are provided, to help customize the behaviour of the types creates:
219//!  - `serde` - when enabled, any type created will derive `Serialize` and `Deserialize`, and will
220//!  be `#[serde(transparent)]`
221//!  - `deref_impls` - some people argue that implementing `Deref` and `DerefMut` on a non-pointer container is
222//!  unidiomatic. Others prefer the ergonomics of being able to call associated functions more
223//!  easily. If `deref_impls` is enabled, microtypes will deref to their inner types
224//!  - `test_impls` - makes secret microtypes easier to work with in test environments by:
225//!    - making their `Debug` implmentation print their actual value instead of `"REDACTED"`
226//!    - making them derive `PartialEq`
227//!  - `secret` - enables secret microtypes, discussed below:
228//!  - `diesel` - if enabled, any attribtes of the form `#[diesel(sql_type = ...)]` will be
229//!  captured, and `FromSql` and `ToSql` implementations will be generated. Note, you will
230//!  generally also want to `#[derive(AsExpression, FromSqlRow)]`
231
232/* TRAIT DEFS */
233
234/// A trait implemented by microtypes
235///
236/// Provides some useful common functions for working with microtypes
237pub trait Microtype {
238    /// The type of the wrapped value
239    ///
240    /// For example, the inner type of an `EmailAddress` could be a `String`
241    type Inner;
242
243    /// Create a microtype from the inner value
244    fn new(inner: Self::Inner) -> Self;
245
246    /// Consume this microtype and return the value it contains
247    fn into_inner(self) -> Self::Inner;
248
249    /// Get a shared reference to the inner value
250    fn inner(&self) -> &Self::Inner;
251
252    /// Get a mutable reference to the inner value
253    fn inner_mut(&mut self) -> &mut Self::Inner;
254
255    /// Explicitly convert from one microtype to another.
256    ///
257    /// This exists as an alternative to `From`/`Into` implementations between different
258    /// microtypes to make conversions explicit
259    fn convert<T: Microtype<Inner = Self::Inner>>(self) -> T;
260}
261
262/// A trait implemented by secret microtypes
263///
264/// Due to their nature, secret microtypes are more restrictive than regular microtypes:
265///  - `inner`, `inner_mut` and `into_inner` are removed, since they can allow accidental use of
266///  the contained secret.
267///  - `SecretMicrotype` requires `ExposeSecret<Self::Inner>`; to use the contained data, use
268///  `.expose_secret()`
269///
270///  The wrapped type must also implement [`secrecy::Zeroize`]
271#[cfg(feature = "secret")]
272pub trait SecretMicrotype: secrecy::ExposeSecret<Self::Inner> {
273    /// The type of the wrapped value
274    /// For example, the inner type of a `Password` could be a `String`
275    type Inner: secrecy::Zeroize;
276
277    /// Create a microtype from the inner value
278    ///
279    /// Note that it is not possible to retrieve the owned value, it can only be read via shared
280    /// reference obtained via `expose_secret()`
281    fn new(inner: Self::Inner) -> Self;
282}
283
284pub use microtype_macro::microtype;
285#[cfg(feature = "secret")]
286pub use secrecy;
287
288#[cfg(test)]
289#[test]
290fn ui() {
291    let t = trybuild::TestCases::new();
292    t.compile_fail("tests/ui/fail/*.rs");
293    t.pass("tests/ui/pass/*.rs");
294
295    #[cfg(feature = "serde")]
296    t.pass("tests/ui/pass/serde/*.rs");
297    #[cfg(feature = "serde")]
298    t.compile_fail("tests/ui/fail/serde/*.rs");
299}