| Crates.io | simple_dto_mapper_derive |
| lib.rs | simple_dto_mapper_derive |
| version | 0.1.1 |
| created_at | 2025-08-09 03:52:52.352646+00 |
| updated_at | 2025-08-09 05:04:38.651074+00 |
| description | A derive macro for mapping DTOs with rename/transform/skip support |
| homepage | |
| repository | https://github.com/sukjaelee/simple_dto_mapper_derive.git |
| max_upload_size | |
| id | 1787558 |
| size | 67,549 |
simple_dto_mapper_derive provides a custom derive macro DtoFrom that generates
impl From<Source> for Target to map model structs into DTOs. It focuses on a
small, explicit set of attributes for clarity and reliability.
#[dto(from = Type)] (required)#[dto(rename = "orig_name")]#[dto(transform_fn = path::to::function)] (owned in → owned out)#[dto(skip)]#[dto(into)] (uses From<Src> for Dst)tests/ui)Default (owned move)
Same name & same type → target = source.field
“Compatible type” means:
#[dto(into)] where From<SourceFieldType> for FieldType exists, or#[dto(transform_fn = ...)] provides an explicit conversionField attributes
#[dto(rename = "orig_name")] — read from another source field name#[dto(transform_fn = path)] — call path(source.orig_name) before assignment#[dto(skip)] — initialize with Default::default()#[dto(into)] — call Into::into(source.orig_name) (requires From)Struct attribute (required)
#[dto(from = Type)] — specify the source struct.
use simple_dto_mapper_derive::DtoFrom;
mod types {
#[derive(Debug)]
pub struct User {
pub id: String,
pub name: String,
pub age: u32,
pub password: String, // intentionally not mapped
pub status: SourceStatus,
}
#[derive(Debug, Clone)]
pub enum SourceStatus { Active, Inactive }
#[derive(Debug, Clone)]
pub enum DtoStatus { Active, Inactive }
// For `#[dto(into)]`
impl From<SourceStatus> for DtoStatus {
fn from(s: SourceStatus) -> Self {
match s { SourceStatus::Active => Self::Active, SourceStatus::Inactive => Self::Inactive }
}
}
// For `#[dto(transform_fn = ...)]`
pub fn mask_name(name: String) -> String {
let ch = name.chars().next().unwrap_or('*');
format!("{ch}***")
}
}
#[derive(DtoFrom, Debug)]
#[dto(from = types::User)]
struct UserDto {
// `rename` + `transform_fn`
#[dto(rename = "name", transform_fn = types::mask_name)]
display_name: String,
// Direct mapping
age: u32,
// skip: default initialize
#[dto(skip)]
note: Option<String>,
// `into`: uses `From<SourceStatus> for DtoStatus`
#[dto(into)]
status: types::DtoStatus,
}
fn main() {
let user = types::User {
id: "u1".into(),
name: "Alice".into(),
age: 30,
password: "secret".into(),
status: types::SourceStatus::Active,
};
let dto: UserDto = user.into();
println!("{dto:?}");
}
Collections and Option do not auto-convert inner elements. Use a transform_fn helper or implement From<Vec<T>> for Vec<U> if you prefer #[dto(into)].
// same type, no transform needed
pub keywords: Vec<String>,
// Vec<SourceTag> → Vec<DtoTag> using a transform function
#[dto(rename = "labels", transform_fn = types::vec_into::<types::SourceTag, types::DtoTag>)]
pub tags: Vec<DtoTag>,
// Option<SourceAuthor> → Option<DtoAuthor>
#[dto(transform_fn = types::opt_into::<types::SourceAuthor, types::DtoAuthor>)]
pub author: Option<DtoAuthor>,
Add this to your Cargo.toml:
[dependencies]
simple_dto_mapper_derive = "0.1.1"
impl From<Source> for Target)transform_fn must be FnOnce(SrcField) -> DstFieldinto requires From<SrcField> for DstFieldskip requires Defaulttests/uiLicensed under the MIT license.