# 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`:
```toml
[dependencies]
describer = "0.3"
```
For third-party type support, enable the relevant features:
```toml
[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
```rust
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:
| `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:
```rust
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 ` | `:
```rust
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
```rust
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:
```rust
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):
```rust
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
```rust
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:
```rust
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:
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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());
```
## Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
## TODO:
- [] Support generic types
- [] Allow remote implementation, like `#[serde(remote = "...")]`