Thanks to visit codestin.com
Credit goes to github.com

Skip to content

naomijub/describer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Describer

This crate provides human-readable type descriptions through the Describer trait and its derive macro.

Overview

describer allows you to generate string representations of Rust types at compile time. This is useful for documentation, debugging, API introspection, code generation, and understanding complex type hierarchies.

Breaking Changes

  • v0.3 redefines completely how the data is output and allows for nested type description;

Features

  • Automatic derive macro for structs and enums. union types are not supported.
  • Built-in implementations for primitives, standard library types, and popular third-party crates.
  • Field-level attributes for customization (skip, as, rename.)
  • Compact notation for common patterns (e.g., Option<T> → T?.)
  • Optional feature flags for third-party integrations.

Installation

Add this to your Cargo.toml:

[dependencies]
describer = "0.3"

For third-party type support, enable the relevant features:

[dependencies]
describer = { version = "0.1.0", features = ["chrono", "uuid", "regex"] }

Available Features

  • chrono - Support for DateTime, NaiveDate, NaiveDateTime, NaiveTime.
  • uuid - Support for Uuid.
  • regex - Support for Regex.
  • http - Support for HTTP types (Request, Response, HeaderMap, etc.).
  • tokio - Support for Tokio's Mutex and RwLock.
  • indexmap - Support for IndexMap and IndexSet.
  • dashmap - Support for DashMap and DashSet.

Basic Usage

use describer::{Describer, Describe};
use std::collections::HashMap;

// Primitives
assert_eq!(i32::describe(), "i32");
assert_eq!(String::describe(), "String");

// Standard library types
assert_eq!(Vec::<u8>::describe(), "[u8]");
assert_eq!(Option::<i32>::describe(), "i32?");
assert_eq!(HashMap::<String, bool>::describe(), "{String: bool}");

// Custom types with derive macro
#[derive(Describe)]
struct User {
    id: u64,
    name: String,
    email: String,
}

assert_eq!(User::describe(), "User { id: u64, name: String, email: String }");

Type Notation

The crate uses a compact notation system for common patterns:

Type Notation Example
Optional T? i32?
Result !{T, E} !{String, i32}
Vec/Slice [T] [u8]
HashMap/BTreeMap {K: V} {String: i32}
HashSet/BTreeSet #{T} #{String}
BinaryHeap ^[T] ^[i32]
Box *T *u8
Arc arc T arc String
Rc rc T rc i32
Cell/RefCell &T &u32
Cow ~T ~String
Range types [T[, [T], etc. [i32[

Derive Macro

Structs

The derive macro works with named, unnamed (tuple), and unit structs:

use describer::{Describer, Describe};

// Named struct
#[derive(Describe)]
struct Point {
    x: f64,
    y: f64,
}
assert_eq!(Point::describe(), "Point { x: f64, y: f64 }");


// Tuple struct
#[derive(Describe)]
struct Color(u8, u8, u8);
assert_eq!(Color::describe(), "Color(u8, u8, u8)");

// Unit struct
#[derive(Describe)]
struct Marker;
assert_eq!(Marker::describe(), "Marker");

Enums

Enums are represented with all their variants separated by |:

use describer::{Describer, Describe};

#[derive(Describe)]
enum Status {
    Active,
    Pending(String),
    Error { code: i32, message: String },
}
assert_eq!(Status::describe(),  "Status #{ Active | Pending(String) | Error { code: i32, message: String } }");

Field Attributes

Customize field descriptions using the #[describe(...)] attribute:

skip - Omit a field

use describer::{Describer, Describe};

#[derive(Describe)]
struct User {
    id: u64,
    #[describe(skip)]
    password: String,
    email: String,
}
assert_eq!(User::describe(), "User { id: u64, email: String }");

with = "..." - Use a custom type name

Replaces the actual type with a custom string:

use describer::{Describer, Describe};

#[derive(Describe)]
struct Config {
    #[describe(with = "Milliseconds")]
    timeout: u64,
    #[describe(with = "URL")]
    server: String,
}
assert_eq!(Config::describe(), "Config { timeout: Milliseconds, server: URL }");

rename = "..." - Rename the field

Changes the field name in the output (note: the actual attribute is rename, though as can be used for type names):

use describer::{Describer, Describe};

#[derive(Describe)]
struct Person {
    #[describe(rename = "identifier")]
    id: u64,
    name: String,
}
assert_eq!(Person::describe(), "Person { identifier: u64, name: String }");

Combining Attributes

use describer::{Describer, Describe};

#[derive(Describe)]
struct ApiResponse {
    #[describe(skip)]
    internal_id: String,
    #[describe(rename = "timestamp", with = "UnixTimestamp")]
    created_at: i64,
    data: Vec<u8>,
}
assert_eq!(ApiResponse::describe(), "ApiResponse { timestamp: UnixTimestamp, data: [u8] }");

Built-in Type Support

Primitives

All Rust primitives are supported: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, String, str

Standard Library Collections

  • Sequences: Vec<T>, VecDeque<T>, LinkedList<T>, BinaryHeap<T>, [T]
  • Maps: HashMap<K, V>, BTreeMap<K, V>
  • Sets: HashSet<T>, BTreeSet<T>

Smart Pointers & Concurrency

  • Owned: Box<T>, Arc<T>, Rc<T>
  • Interior Mutability: Cell<T>, RefCell<T>, Mutex<T>, RwLock<T>
  • Copy-on-Write: Cow<T>

Ranges

All range types: Range<T>, RangeFrom<T>, RangeTo<T>, RangeInclusive<T>, RangeToInclusive<T>, RangeFull

Tuples

Tuples up to 12 elements are supported:

use describer::{Describer, Describe};

assert_eq!(<(i32, String, bool)>::describe(), "(i32, String, bool)");

Third-Party Types (with features)

When enabled via feature flags:

  • chrono: DateTime<Tz>, NaiveDate, NaiveDateTime, NaiveTime
  • uuid: Uuid
  • regex: Regex
  • http: Request<T>, Response<T>, HeaderMap, Method, Uri, StatusCode, Version, Extensions
  • tokio: tokio::sync::Mutex<T>, tokio::sync::RwLock<T>
  • indexmap: IndexMap<K, V>, IndexSet<T>
  • dashmap: DashMap<K, V>, DashSet<T>

Use Cases

API Documentation

Generate human-readable type signatures for API endpoints:

use describer::{Describer, Describe};

#[derive(Describe)]
struct CreateUserRequest {
    username: String,
    email: String,
    #[describe(skip)]
    password: String,
}

fn document_endpoint() {
    println!("POST /users - Body: {}", CreateUserRequest::describe());
    // Output: "POST /users - Body: CreateUserRequest { username: String, email: String }"
}

Schema Generation

use describer::{Describer, Describe};
use std::collections::HashMap;

#[derive(Describe)]
struct DatabaseRecord {
    #[describe(with = "UUID")]
    id: String,
    #[describe(with = "Timestamp")]
    created_at: i64,
    data: HashMap<String, String>,
}

// DatabaseRecord::describe(), "DatabaseRecord { id: UUID, created_at: Timestamp, data: {String: String} }"

Type Introspection

use describer::{Describer, Describe};

#[derive(Describe)]
enum Message {
    Text(String),
    Binary(Vec<u8>),
    Close { code: u16, reason: String },
}

fn log_message_types() {
    println!("Supported message types: {}", Message::describe());
}

Limitations

  • No generics support: The current version does not support generic types in custom structs/enums
  • No lifetime support: Lifetimes are not included in descriptions
  • Unions not supported: Only structs and enums can use the derive macro

Examples

Complex Nested Types

use describer::{Describer, Describe};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;

type ComplexType = Result<Vec<Option<HashMap<String, Arc<Mutex<u64>>>>>, String>;

assert_eq!(
    ComplexType::describe(),
    "!{[{String: arc u64}?], String}"
);

Real-World Example

use describer::{Describer, Describe};
use std::collections::HashMap;

#[derive(Describe)]
struct ApiConfig {
    #[describe(with = "URL")]
    base_url: String,
    #[describe(with = "Milliseconds")]
    timeout: u64,
    #[describe(skip)]
    api_key: String,
    retry_count: u32,
    headers: HashMap<String, String>,
}

#[derive(Describe)]
enum ApiResponse {
    Success {
        data: Vec<u8>,
        #[describe(with = "UnixTimestamp")]
        timestamp: i64,
    },
    Error {
        code: u16,
        message: String,
    },
}

println!("Config: {}", ApiConfig::describe());
// Config: "ApiConfig { base_url: URL, timeout: Milliseconds, retry_count: u32, headers: {String: String} }"
assert_eq!(ApiConfig::describe(), "ApiConfig { base_url: URL, timeout: Milliseconds, retry_count: u32, headers: {String: String} }");

println!("Response: {}", ApiResponse::describe());
// Response: "ApiResponse { Success { data: [u8], timestamp: UnixTimestamp } | Error { code: u16, message: String } }"
assert_eq!(ApiResponse::describe(), "ApiResponse #{ Success { data: [u8], timestamp: UnixTimestamp } | Error { code: u16, message: String } }");

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

TODO:

  • [] Support generic types
  • [] Allow remote implementation, like #[serde(remote = "...")]

About

Helper derive macro to Describe rust Structs and Enums

Topics

Resources

License

Stars

Watchers

Forks

Languages