micro is a lightweight Rust crate providing the FromDto procedural macro. It simplifies the conversion between Data Transfer Objects (DTOs) and your internal domain models by automatically generating From trait implementations.
- Struct Mapping: Automatically maps named fields from a DTO to your domain struct.
- Enum Mapping: Supports Unit, Unnamed (tuple), and Named enum variants.
- Collection Support: Specialized handling for
Vec<T>andOption<T>, automatically calling.into()on inner items. - Generic Support: Correctly handles generic types (e.g.,
List<T>orResponse<T>) and generates appropriate trait bounds. - Attribute-Driven: Define multiple source types for a single domain model using the
#[from(...)]attribute.
Add this to your Cargo.toml:
[dependencies]
micro = "<version>" # Update <version> to latestIf field names match, FromDto generates the boilerplate to convert from your DTO to your Domain model, calling .into() on every field to handle nested conversions.
use micro::FromDto;
// The DTO (External Source)
pub mod external {
pub struct UserDto {
pub id: i64,
pub username: String,
}
}
// The Domain Model
#[derive(FromDto)]
#[from(external::UserDto)]
pub struct User {
pub id: i64,
pub username: String,
}The macro handles generic parameters and complex enum structures. It automatically strips generics from paths in match arms to ensure compatibility with stable Rust.
#[derive(FromDto)]
#[from(external::ApiResponse<T>)]
pub enum Response<T> {
Success(T),
Empty,
Error(ErrorWrapper),
}FromDto detects Vec and Option types to ensure that inner types are converted correctly using the .into_iter().map(...).collect() pattern.
#[derive(FromDto)]
#[from(external::LibraryDto)]
pub struct Library {
pub tags: Vec<String>, // Handled via .collect()
pub metadata: Option<Metadata>, // Handled via .map(Into::into)
}Converting APIs often involves deeply nested optional collections, such as Option<Vec<T>>. Since Rust's Into trait does not automatically reach through multiple layers of containers, FromDto detects these patterns and generates the necessary mapping code automatically.
#[derive(FromDto)]
#[from(external::TrackDto)]
pub struct Track {
// Generates: value.contributors.map(|v| v.into_iter().map(Into::into).collect())
pub contributors: Option<Vec<Artist>>,
}The FromDto macro inspects your struct or enum at compile time and generates a From<Source> implementation:
- Generic Splitting: Uses
split_for_impl()to ensure trait bounds likeimpl<T> From<Source<T>>forTarget<T>are correctly declared. - Path Cleaning: It strips generic arguments from enum variants specifically within match arms. This avoids the "qualified paths in this context is experimental" error on stable Rust.
- Recursive Conversion: It assumes that inner types also implement From (or are decorated with
FromDto) and chains them using.into().