| Crates.io | describer |
| lib.rs | describer |
| version | 0.3.1 |
| created_at | 2025-07-24 22:47:05.47953+00 |
| updated_at | 2026-01-23 20:06:42.911816+00 |
| description | Struct describing helper |
| homepage | |
| repository | https://github.com/naomijub/describer |
| max_upload_size | |
| id | 1767004 |
| size | 45,898 |
This crate provides human-readable type descriptions through the Describer trait and its derive macro.
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.
v0.3 redefines completely how the data is output and allows for nested type description;structs and enums. union types are not supported.skip, as, rename.)Option<T> → T?.)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"] }
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.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 }");
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[ |
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 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 } }");
Customize field descriptions using the #[describe(...)] attribute:
skip - Omit a fielduse 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 nameReplaces 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 fieldChanges 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 }");
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] }");
All Rust primitives are supported: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, String, str
Vec<T>, VecDeque<T>, LinkedList<T>, BinaryHeap<T>, [T]HashMap<K, V>, BTreeMap<K, V>HashSet<T>, BTreeSet<T>Box<T>, Arc<T>, Rc<T>Cell<T>, RefCell<T>, Mutex<T>, RwLock<T>Cow<T>All range types: Range<T>, RangeFrom<T>, RangeTo<T>, RangeInclusive<T>, RangeToInclusive<T>, RangeFull
Tuples up to 12 elements are supported:
use describer::{Describer, Describe};
assert_eq!(<(i32, String, bool)>::describe(), "(i32, String, bool)");
When enabled via feature flags:
DateTime<Tz>, NaiveDate, NaiveDateTime, NaiveTimeUuidRegexRequest<T>, Response<T>, HeaderMap, Method, Uri, StatusCode, Version, Extensionstokio::sync::Mutex<T>, tokio::sync::RwLock<T>IndexMap<K, V>, IndexSet<T>DashMap<K, V>, DashSet<T>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 }"
}
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} }"
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());
}
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}"
);
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 } }");
Contributions are welcome! Please feel free to submit issues or pull requests.
#[serde(remote = "...")]