field_name 0.2.0

A proc-macro for exposing a struct's field names at runtime.
Documentation

field_name

Latest Version

Stop hardcoding strings. Start using the type system.

We've all been there. You're writing a MongoDB query, a raw SQL statement, or some custom JSON patch logic. You type "user_name" into your query builder. Later, you refactor your Rust struct to use username.

The code compiles. The tests pass (mostly). But in production, your query returns nothing because the string literal didn't update itself.

field_name fixes this by making your field names available as compile-time constants.

The Problem

// ❌ The "Stringly Typed" Way
// If you rename `id` to `uuid` in the struct, this line acts like nothing happened.
// You won't know it's broken until runtime.
let query = doc! { "_id": "12345" };

The Solution

// ✅ The "Strongly Typed" Way
// If you rename `id` to `uuid` in the struct, this won't compile.
// You catch the bug immediately.
let query = doc! { User::ID: "12345" };

Installation

Add this to your Cargo.toml:

[dependencies]
field_name = "0.2"

How to use it

1. For Structs (The Mongo/SQL Use Case)

Just derive FieldNames. It generates a public constant for every field (uppercased) and a FIELDS array containing all of them.

use field_name::FieldNames;

#[derive(FieldNames)]
struct User {
    // 1. Rename support: Map Rust's "id" to Mongo's "_id"
    #[field_name(rename = "_id")] 
    id: u64,

    username: String,

    // 2. Skip support: Don't generate constants for internal fields
    #[field_name(skip)] 
    password_hash: String,
}

fn main() {
    // Use the individual constants for queries
    assert_eq!(User::ID, "_id");
    assert_eq!(User::USERNAME, "username");

    // Use the list for validation or schema generation
    assert_eq!(User::FIELDS, ["_id", "username"]);
    
    // Attempting to access User::PASSWORD_HASH will fail to compile!
}

2. For Enums (The State Machine Use Case)

Derive VariantNames to get string representations of your variants without instantiating them.

use field_name::VariantNames;

#[derive(VariantNames)]
enum Status {
    Pending,
    
    #[variant_name(rename = "in_progress")]
    Processing,
    
    Completed,
}

fn main() {
    assert_eq!(Status::PENDING, "Pending");
    assert_eq!(Status::PROCESSING, "in_progress");
    
    // Great for logging or quick checks
    if some_api_string == Status::PROCESSING {
        println!("We are moving!");
    }
}

Why not just use Serde?

Serde is amazing for serialization, but it doesn't expose the names of the fields as constants you can use in code. If you need to construct a dictionary, a specific SQL query, or a dynamic generic function that iterates over valid field names, Serde can't help you there. field_name fills that gap.

Configuration Cheat Sheet

Attribute Where? What it does
#[field_name(rename = "x")] Struct Field Changes the constant value to "x". Useful for _id or camelCase APIs.
#[field_name(skip)] Struct Field Ignores this field. No constant or array entry will be generated.
#[variant_name(rename = "x")] Enum Variant Same as above, but for Enums.
#[variant_name(skip)] Enum Variant Same as above, but for Enums.

License

MIT. Do whatever you want with it. Happy coding!