field_name/lib.rs
1//! # field_name
2//!
3//! A procedural macro crate that allows you to access struct field names and enum variant names
4//! as compile-time string constants.
5//!
6//! ## Motivation
7//!
8//! When writing code that interacts with databases (like MongoDB), serialization formats (JSON),
9//! or dynamic query builders, developers often hardcode field names as strings:
10//!
11//! ```rust,ignore
12//! // ❌ Error-prone: if the struct field changes, this string becomes invalid.
13//! let query = doc! { "userName": "alice" };
14//! ```
15//!
16//! `field_name` solves this by generating associated constants for your types.
17//! If you rename a field in your struct, your code will fail to compile, saving you from runtime errors.
18//!
19//! ```rust,ignore
20//! // ✅ Type-safe: compiler ensures this field exists.
21//! let query = doc! { User::USER_NAME: "alice" };
22//! ```
23//!
24//! ## Features
25//!
26//! - **Struct Fields**: Generates `pub const FIELD_NAME` for every field.
27//! - **Enum Variants**: Generates `pub const VARIANT_NAME` for every variant.
28//! - **Renaming**: Support for mapping Rust field names to wire formats (e.g., `id` -> `_id`).
29//! - **Skipping**: Exclude internal fields or variants from generation.
30//! - **Lists**: Generates a `FIELDS` or `VARIANTS` array containing all valid names.
31//!
32//! ## Usage
33//!
34//! Add this to your `Cargo.toml`:
35//!
36//! ```toml
37//! [dependencies]
38//! field_name = "0.1"
39//! ```
40//!
41//! ### Deriving Struct Field Names
42//!
43//! Use `#[derive(FieldNames)]` on named structs.
44//!
45//! ```rust
46//! use field_name::FieldNames;
47//!
48//! #[derive(FieldNames)]
49//! struct User {
50//! username: String,
51//! email: String,
52//!
53//! #[field_name(rename = "_id")]
54//! id: u64,
55//!
56//! #[field_name(skip)]
57//! internal_cache: String,
58//! }
59//!
60//! fn main() {
61//! // Access individual field names as constants
62//! assert_eq!(User::USERNAME, "username");
63//! assert_eq!(User::EMAIL, "email");
64//! assert_eq!(User::ID, "_id");
65//!
66//! // Access all fields as a list
67//! assert_eq!(User::FIELDS, ["username", "email", "_id"]);
68//!
69//! // 'internal_cache' is skipped
70//! assert!(!User::FIELDS.contains(&"internal_cache"));
71//! }
72//! ```
73//!
74//! ### Deriving Enum Variant Names
75//!
76//! Use `#[derive(VariantNames)]` on enums.
77//!
78//! ```rust
79//! use field_name::VariantNames;
80//!
81//! #[derive(VariantNames)]
82//! enum ConnectionState {
83//! Connected,
84//!
85//! #[variant_name(rename = "disconnected_by_server")]
86//! Disconnected,
87//!
88//! Error(String),
89//! }
90//!
91//! fn main() {
92//! assert_eq!(ConnectionState::CONNECTED, "Connected");
93//! assert_eq!(ConnectionState::DISCONNECTED, "disconnected_by_server");
94//! assert_eq!(ConnectionState::ERROR, "Error");
95//!
96//! assert_eq!(ConnectionState::VARIANTS, ["Connected", "disconnected_by_server", "Error"]);
97//! }
98//! ```
99//!
100//! ## Configuration
101//!
102//! The macros can be configured using the `#[field_name(...)]` (for structs) and
103//! `#[variant_name(...)]` (for enums) attributes.
104//!
105//! | Attribute | Description |
106//! |-----------|-------------|
107//! | `skip` | Excludes the field/variant from the `FIELDS`/`VARIANTS` array and does not generate a constant. |
108//! | `rename = "name"` | Uses the provided string value instead of the Rust identifier. Useful for `_id` or `camelCase`. |
109
110extern crate proc_macro;
111
112use darling::FromDeriveInput;
113use quote::quote;
114use syn::{parse_macro_input, DeriveInput};
115
116mod fields;
117mod variants;
118
119/// Derives associated constants and a `FIELDS` array for struct fields.
120///
121/// See the [crate-level documentation](crate) for usage examples.
122#[proc_macro_derive(FieldNames, attributes(field_name))]
123pub fn derive_field_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
124 fields::Receiver::from_derive_input(&parse_macro_input!(input as DeriveInput))
125 .map(|receiver| quote!(#receiver))
126 .unwrap_or_else(|err| err.write_errors())
127 .into()
128}
129
130/// Derives associated constants and a `VARIANTS` array for enum variants.
131///
132/// See the [crate-level documentation](crate) for usage examples.
133#[proc_macro_derive(VariantNames, attributes(variant_name))]
134pub fn derive_variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
135 variants::Receiver::from_derive_input(&parse_macro_input!(input as DeriveInput))
136 .map(|receiver| quote!(#receiver))
137 .unwrap_or_else(|err| err.write_errors())
138 .into()
139}