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;
37mod path;
38#[cfg(feature = "secret")]
39mod secret;
40#[cfg(feature = "ulid")]
41mod ulid;
42#[cfg(feature = "url")]
43mod url;
44#[cfg(feature = "uuid")]
45mod uuid;
46
47/// Generate a new type for a string
48///
49/// The `name!` macro generates a new type that is backed by a `String`. The new type implements
50/// common traits like `Display` and `From<&str>` and `From<String>`. The inner value can be
51/// accessed using the `get` method.
52///
53/// # Example
54///
55/// ```
56/// use typed_fields::name;
57///
58/// // Define a new type that is backed by a `String`
59/// name!(Login);
60///
61/// // Create a new `UserId` from a `&str`
62/// let id = Login::new("jdno");
63///
64/// // Common traits like `Display` are automatically implemented for the type
65/// println!("Login: {}", id);
66/// ```
67#[proc_macro]
68pub fn name(input: TokenStream) -> TokenStream {
69    name::name_impl(input)
70}
71
72/// Generate a new type for a number
73///
74/// The `number!` macro generates a new type that is backed by a numeric primitive. By default it
75/// uses `i64`, but you can optionally provide a different backing type, e.g. `u64`. The new type
76/// implements common traits like `Display` and `From<T>`, where `T` is the chosen backing type. The
77/// inner value can be accessed using the `get` method.
78///
79/// # Examples
80///
81/// ```
82/// use typed_fields::number;
83///
84/// number!(UserId);
85///
86/// let id = UserId::new(42);
87/// println!("User ID: {}", id);
88/// ```
89///
90/// The default backing type is `i64`, but this can be changed by passing another type as the second
91/// argument:
92///
93/// ```
94/// use typed_fields::number;
95///
96/// number!(Revision, u64);
97///
98/// let rev = Revision::new(42u64);
99/// assert_eq!(42u64, rev.get());
100/// ```
101#[proc_macro]
102pub fn number(input: TokenStream) -> TokenStream {
103    number::number_impl(input)
104}
105
106/// Generate a new type for a path
107///
108/// The `path!` macro generates a new type that is backed by a `PathBuf`. The new type implements
109/// common traits like `Display` and `From<&Path>`. The inner value can be accessed using the
110/// `get` method.
111///
112/// # Examples
113///
114/// ```
115/// use std::path::PathBuf;
116/// use typed_fields::path;
117///
118/// path!(MyPath);
119///
120/// let path = MyPath::new(PathBuf::from("src"));
121/// println!("My path: {}", path);
122/// ```
123#[proc_macro]
124pub fn path(input: TokenStream) -> TokenStream {
125    path::path_impl(input)
126}
127
128/// Generate a new type for a secret
129///
130/// The `secret!` macro generates a new type for secrets such as passwords and API tokens. The type
131/// uses the [`secrecy`](https://crates.io/crates/secrecy) crate internally to prevent accidentally
132/// leaking the inner value in debug or log statements.
133///
134/// The new type implements common traits like `Display` and `From<&str>` and `From<String>`. The
135/// inner value can be revealed using the `expose` method.
136///
137/// # Example
138///
139/// ```rust
140/// use typed_fields::secret;
141///
142/// secret!(ApiToken);
143///
144/// let token: ApiToken = "super-secret-api-token".into();
145/// let header = format!("Authorization: Bearer {}", token.expose());
146/// ```
147#[cfg(feature = "secret")]
148#[proc_macro]
149pub fn secret(input: TokenStream) -> TokenStream {
150    secret::secret_impl(input)
151}
152
153/// Generate a new type for a ULID
154///
155/// The `ulid!` macro generates a new type that is backed by a `Ulid` from the [`ulid`] crate. The
156/// new type implements common traits like `Display` and `From<&str>` and `From<String>`. The inner
157/// value can be accessed using the `get` method.
158///
159/// # Example
160///
161/// ```rust
162/// use typed_fields::ulid;
163///
164/// ulid!(UserId);
165///
166/// fn main() -> Result<(), Box<dyn std::error::Error>> {
167///     let id: UserId = "01ARZ3NDEKTSV4RRFFQ69G5FAV".try_into()?;
168///     # Ok(())
169///     // Do something with the URL...
170/// }
171/// ```
172///
173/// [`ulid`]: https://crates.io/crates/ulid
174#[cfg(feature = "ulid")]
175#[proc_macro]
176pub fn ulid(input: TokenStream) -> TokenStream {
177    ulid::ulid_impl(input)
178}
179
180/// Generate a new type for a URL
181///
182/// The `url!` macro generates a new type that is backed by a `Url` from the [`url`] crate. The new
183/// type implements common traits like `Display` and `TryFrom<&str>` and `TryFrom<String>`. The
184/// inner value can be accessed using the `get` method.
185///
186/// # Example
187///
188/// ```rust
189/// use typed_fields::url;
190///
191/// url!(BackendUrl);
192///
193/// fn main() -> Result<(), Box<dyn std::error::Error>> {
194///     let url: BackendUrl = "https://api.example.com".try_into()?;
195///     # Ok(())
196///     // Do something with the URL...
197/// }
198/// ```
199///
200/// [`url`]: https://crates.io/crates/url
201#[cfg(feature = "url")]
202#[proc_macro]
203pub fn url(input: TokenStream) -> TokenStream {
204    url::url_impl(input)
205}
206
207/// Generate a new type for a UUID
208///
209/// The `uuid!` macro generates a new type that is backed by a `Uuid` from the [`uuid`] crate. The
210/// new type implements common traits like `Display` and `TryFrom<&str>` and `TryFrom<String>`. The
211/// inner value can be accessed using the `get` method.
212///
213/// # Example
214///
215/// ```rust
216/// use typed_fields::uuid;
217///
218/// uuid!(UserId);
219///
220/// fn main() -> Result<(), Box<dyn std::error::Error>> {
221///     let id: UserId = "67e55044-10b1-426f-9247-bb680e5fe0c8".try_into()?;
222///     # Ok(())
223///     // Do something with the URL...
224/// }
225/// ```
226///
227/// [`uuid`]: https://crates.io/crates/uuid
228#[cfg(feature = "uuid")]
229#[proc_macro]
230pub fn uuid(input: TokenStream) -> TokenStream {
231    uuid::uuid_impl(input)
232}
233
234/// The token stream of each macro invocation
235///
236/// This struct represents the token stream of each macro invocation. Consider
237/// the following example:
238///
239/// ```rust
240/// use typed_fields::name;
241///
242/// name!(
243///     /// This is a doc comment
244///     TestName
245/// );
246/// ```
247///
248/// In this example, `attrs` will contain the doc comment and `ident` will
249/// contain the identifier `TestName`. More attributes can be added by the user,
250/// e.g. additional derives.
251struct Input {
252    attrs: Vec<Attribute>,
253    ident: Ident,
254}
255
256impl Parse for Input {
257    fn parse(input: ParseStream) -> syn::Result<Self> {
258        let attrs = input.call(Attribute::parse_outer)?;
259        let ident: Ident = input.parse()?;
260
261        Ok(Self { attrs, ident })
262    }
263}