typed_fields/
lib.rs

1//! This crate contains a set of macros that can be used to generate strongly-typed fields for
2//! structs. The macros implement the [newtype] pattern, which allows the compiler to enforce type
3//! safety while still making it easy to convert the fields to and from their underlying
4//! representation.
5//!
6//! # Example
7//!
8//! ```rust
9//! use typed_fields::number;
10//!
11//! // Define a new type that is backed by an `i64`
12//! number!(UserId);
13//!
14//! // Create a new `UserId` from an `i64`
15//! let id = UserId::new(42);
16//!
17//! // Common traits like `Display` are automatically implemented for the type
18//! println!("User ID: {}", id);
19//! ```
20//!
21//! [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
22
23// Code in this library should never panic, which is why we are denying the use of both `expect` and
24// `unwrap`. Instead, functions must return a `Result` that can be handled by the caller.
25#![warn(clippy::expect_used)]
26#![warn(clippy::unwrap_used)]
27// All public items in this library must have documentation.
28#![warn(missing_docs)]
29
30use proc_macro::TokenStream;
31use proc_macro2::Ident;
32use syn::Attribute;
33use syn::parse::{Parse, ParseStream};
34
35mod name;
36mod number;
37#[cfg(feature = "secret")]
38mod secret;
39#[cfg(feature = "ulid")]
40mod ulid;
41#[cfg(feature = "url")]
42mod url;
43#[cfg(feature = "uuid")]
44mod uuid;
45
46/// Generate a new type for a string
47///
48/// The `name!` macro generates a new type that is backed by a `String`. The new type implements
49/// common traits like `Display` and `From<&str>` and `From<String>`. The inner value can be
50/// accessed using the `get` method.
51///
52/// # Example
53///
54/// ```
55/// use typed_fields::name;
56///
57/// // Define a new type that is backed by a `String`
58/// name!(Login);
59///
60/// // Create a new `UserId` from a `&str`
61/// let id = Login::new("jdno");
62///
63/// // Common traits like `Display` are automatically implemented for the type
64/// println!("Login: {}", id);
65/// ```
66#[proc_macro]
67pub fn name(input: TokenStream) -> TokenStream {
68    name::name_impl(input)
69}
70
71/// Generate a new type for a number
72///
73/// The `number!` macro generates a new type that is backed by a numeric primitive. By default it
74/// uses `i64`, but you can optionally provide a different backing type, e.g. `u64`. The new type
75/// implements common traits like `Display` and `From<T>`, where `T` is the chosen backing type. The
76/// inner value can be accessed using the `get` method.
77///
78/// # Examples
79///
80/// ```
81/// use typed_fields::number;
82///
83/// number!(UserId);
84///
85/// let id = UserId::new(42);
86/// println!("User ID: {}", id);
87/// ```
88///
89/// The default backing type is `i64`, but this can be changed by passing another type as the second
90/// argument:
91///
92/// ```
93/// use typed_fields::number;
94///
95/// number!(Revision, u64);
96///
97/// let rev = Revision::new(42u64);
98/// assert_eq!(42u64, rev.get());
99/// ```
100#[proc_macro]
101pub fn number(input: TokenStream) -> TokenStream {
102    number::number_impl(input)
103}
104
105/// Generate a new type for a secret
106///
107/// The `secret!` macro generates a new type for secrets such as passwords and API tokens. The type
108/// uses the [`secrecy`](https://crates.io/crates/secrecy) crate internally to prevent accidentally
109/// leaking the inner value in debug or log statements.
110///
111/// The new type implements common traits like `Display` and `From<&str>` and `From<String>`. The
112/// inner value can be revealed using the `expose` method.
113///
114/// # Example
115///
116/// ```rust
117/// use typed_fields::secret;
118///
119/// secret!(ApiToken);
120///
121/// let token: ApiToken = "super-secret-api-token".into();
122/// let header = format!("Authorization: Bearer {}", token.expose());
123/// ```
124#[cfg(feature = "secret")]
125#[proc_macro]
126pub fn secret(input: TokenStream) -> TokenStream {
127    secret::secret_impl(input)
128}
129
130/// Generate a new type for a ULID
131///
132/// The `ulid!` macro generates a new type that is backed by a `Ulid` from the [`ulid`] crate. The
133/// new type implements common traits like `Display` and `From<&str>` and `From<String>`. The inner
134/// value can be accessed using the `get` method.
135///
136/// # Example
137///
138/// ```rust
139/// use typed_fields::ulid;
140///
141/// ulid!(UserId);
142///
143/// fn main() -> Result<(), Box<dyn std::error::Error>> {
144///     let id: UserId = "01ARZ3NDEKTSV4RRFFQ69G5FAV".try_into()?;
145///     # Ok(())
146///     // Do something with the URL...
147/// }
148/// ```
149///
150/// [`ulid`]: https://crates.io/crates/ulid
151#[cfg(feature = "ulid")]
152#[proc_macro]
153pub fn ulid(input: TokenStream) -> TokenStream {
154    ulid::ulid_impl(input)
155}
156
157/// Generate a new type for a URL
158///
159/// The `url!` macro generates a new type that is backed by a `Url` from the [`url`] crate. The new
160/// type implements common traits like `Display` and `TryFrom<&str>` and `TryFrom<String>`. The
161/// inner value can be accessed using the `get` method.
162///
163/// # Example
164///
165/// ```rust
166/// use typed_fields::url;
167///
168/// url!(BackendUrl);
169///
170/// fn main() -> Result<(), Box<dyn std::error::Error>> {
171///     let url: BackendUrl = "https://api.example.com".try_into()?;
172///     # Ok(())
173///     // Do something with the URL...
174/// }
175/// ```
176///
177/// [`url`]: https://crates.io/crates/url
178#[cfg(feature = "url")]
179#[proc_macro]
180pub fn url(input: TokenStream) -> TokenStream {
181    url::url_impl(input)
182}
183
184/// Generate a new type for a UUID
185///
186/// The `uuid!` macro generates a new type that is backed by a `Uuid` from the [`uuid`] crate. The
187/// new type implements common traits like `Display` and `TryFrom<&str>` and `TryFrom<String>`. The
188/// inner value can be accessed using the `get` method.
189///
190/// # Example
191///
192/// ```rust
193/// use typed_fields::uuid;
194///
195/// uuid!(UserId);
196///
197/// fn main() -> Result<(), Box<dyn std::error::Error>> {
198///     let id: UserId = "67e55044-10b1-426f-9247-bb680e5fe0c8".try_into()?;
199///     # Ok(())
200///     // Do something with the URL...
201/// }
202/// ```
203///
204/// [`uuid`]: https://crates.io/crates/uuid
205#[cfg(feature = "uuid")]
206#[proc_macro]
207pub fn uuid(input: TokenStream) -> TokenStream {
208    uuid::uuid_impl(input)
209}
210
211/// The token stream of each macro invocation
212///
213/// This struct represents the token stream of each macro invocation. Consider
214/// the following example:
215///
216/// ```rust
217/// use typed_fields::name;
218///
219/// name!(
220///     /// This is a doc comment
221///     TestName
222/// );
223/// ```
224///
225/// In this example, `attrs` will contain the doc comment and `ident` will
226/// contain the identifier `TestName`. More attributes can be added by the user,
227/// e.g. additional derives.
228struct Input {
229    attrs: Vec<Attribute>,
230    ident: Ident,
231}
232
233impl Parse for Input {
234    fn parse(input: ParseStream) -> syn::Result<Self> {
235        let attrs = input.call(Attribute::parse_outer)?;
236        let ident: Ident = input.parse()?;
237
238        Ok(Self { attrs, ident })
239    }
240}