Skip to content

Latest commit

 

History

History
128 lines (110 loc) · 4.15 KB

README.md

File metadata and controls

128 lines (110 loc) · 4.15 KB

ser_mapper

crates.io docs.rs

Serialization Mapper.

Implement mapped DTO serialzation wrapper for DBO/Model without mapping objects.

Instead of mapping DBO/Model to DTO then serializing DTO, this macro will help you create the skeleton of DTO and assign and/or map the values you want each field of DTO to have from DBO/Model.

Example

Breaking change from 0.1.0: Implicit lambda is replaced with lambda token expression.

From below instance, user_id: String = id => TableId::get_id, is
replaced with user_id: String = id => |id: &TableId| -> &str { &id.id }.

Notice unlike actual lambda, this lambda token expression treats &TableId and &str as same lifetimes.
This lambda token expression is expanded into function to support same lifetimes, removing need of TableId::get_id.

Here's how macro is used (see full sample below):

impl_dto!(
    #[derive(Debug)]    // derives
    pub struct UserResponse<UserDbo> {    // Dto<Dbo/Model>
        // dto_field: type = dbo_field Optional(=> |var: dbo_type| -> ser_type { expr }),
        user_id: String = id => |id: &TableId| -> &str { &id.id },
        first_name: String = full_name => |n: &str| -> &str { n.split(" ").nth(0).unwrap() },
        last_name: String = full_name => |n: &str| -> &str { n.split(" ").nth(1).unwrap() },
        email_id: String = email,
        age: u8 = age => |a: &Age| -> &u8 { &a.0 },
    }
);

The impl_dto macro creates the following wrappers (see full sample below):

// ...
struct UserResponse { id: String, first_name: String, last_name: String, email_id: String, age: u8 };

/// All of the below implements [`serde::Serialize`]
struct _UserResponse(pub UserDbo);
struct _UserResponseRef<'a>(pub &'a UserDbo);
struct _UserResponseOption(pub Option<UserDbo>);
struct _UserResponseRefOption<'a>(pub Option<&'a UserDbo>);
struct _UserResponseOptionRef<'a>(pub &'a Option<UserDbo>);
struct _UserResponseVec(pub Vec<UserDbo>);
struct _UserResponseRefVec<'a>(pub Vec<&'a UserDbo>);
struct _UserResponseVecRef<'a>(pub &'a Vec<UserDbo>);
// ...

Sample code:

mod datastore {
    #[derive(Debug)]
    pub struct TableId {
        pub table: String,
        pub id: String,
    }

    #[derive(Debug)]
    pub struct Age(pub u8);

    #[derive(Debug)]
    pub struct UserDbo {
        pub id: TableId,
        pub full_name: String,
        pub email: String,
        pub password: String,
        pub hash: String,
        pub age: Age,
    }

    pub fn get_dbo() -> UserDbo {
        UserDbo {
            id: TableId {
                table: String::from("User"),
                id: String::from("abcd_123"),
            },
            full_name: String::from("John Doe"),
            email: String::from("[email protected]"),
            password: String::from("password"),
            hash: String::from("hash"),
            age: Age(69),
        }
    }
}

mod dto {
    use super::datastore::*;
    use ser_mapper::impl_dto;

    impl_dto!(
        #[derive(Debug)]
        pub struct UserResponseDto<UserDbo> {
            user_id: String = id => |id: &TableId| -> &str { &id.id },
            first_name: String = full_name => |n: &str| -> &str { n.split(" ").nth(0).unwrap() },
            last_name: String = full_name => |n: &str| -> &str { n.split(" ").nth(1).unwrap() },
            email_id: String = email,
            age: u8 = age => |a: &Age| -> &u8 { &a.0 },
        }
    );
}

fn main() {
    // Some DBO retrieved from DB
    let dbo = datastore::get_dbo();

    // Instead of mapping, use either of the wrappers created by `impl_dto` macro
    let dto = dto::_UserResponse(dbo);

    // and it will serialize as written, for response
    assert_eq!(
        r#"{
  "user_id": "abcd_123",
  "first_name": "John",
  "last_name": "Doe",
  "email_id": "[email protected]",
  "age": 69
}"#,
        serde_json::to_string_pretty(&dto).unwrap()
    );
}