Giter VIP home page Giter VIP logo

model-mapper's Introduction

Model Mapper

This library provides a macro to implement functions to convert between types (both enums and structs) without boilerplate.

It also provides a with module containing some utilities to convert between types that does not implement Into trait.

Examples

The most common use case for this crate is to map between domain entities on services and externally-faced models or DTOs.

#[derive(Mapper)]
#[mapper(from, ty = Entity)]
pub struct Model {
    id: i64,
    name: String,
}

The macro expansion above would generate something like:

impl From<Entity> for Model {
    fn from(Entity { id, name }: Entity) -> Self {
        Self {
            id: Into::into(id),
            name: Into::into(name),
        }
    }
}

Because types doesn't always fit like a glove, you can provide additional fields on runtime, at the cost of not being able to use the From trait:

pub mod service {
    pub struct UpdateUserInput {
        pub user_id: i64,
        pub name: Option<String>,
        pub surname: Option<String>,
    }
}

#[derive(Mapper)]
#[mapper(
    into(custom = "into_update_user"),
    ty = service::UpdateUserInput,
    add(field = user_id, ty = i64),
    add(field = surname, default(value = None))
)]
pub struct UpdateProfileRequest {
    pub name: String,
}

Would generate something like:

impl UpdateProfileRequest {
    /// Builds a new [service::UpdateUserInput] from a [UpdateProfileRequest]
    pub fn into_update_user(self, user_id: i64) -> service::UpdateUserInput {
        let UpdateProfileRequest { name } = self;
        service::UpdateUserInput {
            user_id,
            surname: None,
            name: Into::into(name),
        }
    }
}

Other advanced use cases are available on the examples folder.

Usage

A mapper attribute is required at type-level and it's optional at field or variant level.

The following attributes are available.

  • Type level attributes:

    • ty = PathType (mandatory): The other type to derive the conversion
    • from (optional): Whether to derive From the other type for self
      • custom (optional): Derive a custom function instead of the trait
      • custom = from_other (optional): Derive a custom function instead of the trait, with the given name
    • into (optional): Whether to derive From self for the other type
      • custom (optional): Derive a custom function instead of the trait
      • custom = from_other (optional): Derive a custom function instead of the trait, with the given name
    • try_from (optional): Whether to derive TryFrom the other type for self
      • custom (optional): Derive a custom function instead of the trait
      • custom = from_other (optional): Derive a custom function instead of the trait, with the given name
    • try_into (optional): Whether to derive TryFrom self for the other type
      • custom (optional): Derive a custom function instead of the trait
      • custom = from_other (optional): Derive a custom function instead of the trait, with the given name
    • add (optional, multiple): Additional fields (for structs with named fields) or variants (for enums) the other type has and this one doesn't ¹
      • field = other_field (mandatory): The field or variant name
      • ty = bool (optional): The field type, mandatory for into and try_into if no default value is provided
      • default (optional): The field or variant will be populated using Default::default() (mandatory for enums, with or without value)
        • value = true (optional): The field or variant will be populated with the given expression instead
    • ignore_extra (optional): Whether to ignore all extra fields (for structs) or variants (for enums) of the other type ²
  • Variant level attributes:

    • rename = OtherVariant (optional): To rename this variant on the other enum
    • add (optional, multiple): Additional fields of the variant that the other type variant has and this one doesn't ¹
      • field = other_field (mandatory): The field name
      • ty = bool (optional): The field type, mandatory for into and try_into if no default value is provided
      • default (optional): The field or variant will be populated using Default::default()
        • value = true (optional): The field or variant will be populated with the given expression instead
    • skip (optional): Whether to skip this variant because the other enum doesn't have it
      • default (mandatory): The field or variant will be populated using Default::default()
        • value = get_default_value() (optional): The field or variant will be populated with the given expression instead
    • ignore_extra (optional): Whether to ignore all extra fields of the other variant (only valid for from and try_from) ²
  • Field level attributes:

    • rename = other_name (optional): To rename this field on the other type
    • skip (optional): Whether to skip this field because the other type doesn't have it
      • default (optional): The field or variant will be populated using Default::default()
        • value = get_default_value() (optional): The field or variant will be populated with the given expression instead
    • with = mod::my_function (optional): If the field type doesn't implement Into or TryInto the other, this property allows you to customize the behavior by providing a conversion function
    • into_with = mod::my_function (optional): The same as above but only for the into or try_into derives
    • from_with = mod::my_function (optional): The same as above but only for the from or try_from derives

¹ When providing additional fields without defaults, the From and TryFrom traits can't be derived and a custom function will be required instead. When deriving into or try_into, the ty must be provided as well.

² When ignoring fields or variants it might be required that the enum or the struct implements Default in order to properly populate it.

Multiple derives

When deriving conversions for a single type, attributes can be set directly:

#[mapper(from, into, ty = OtherType, add(field = field_1, default), add(field = field_2, default))]

But we can also derive conversions for multiple types by wrapping the properties on a derive attribute:

#[mapper(derive(try_into, ty = OtherType, add(field = field_1, default)))]
#[mapper(derive(from, ty = YetAnotherType))]

If multiple conversions are involved, both variant and field level attributes can also be wrapped in a when attribute and must set the ty they refer to:

#[mapper(when(ty = OtherType, with = TryIntoMapper::try_map_removing_option))]
#[mapper(when(ty = YetAnotherType, skip(default)))]

model-mapper's People

Contributors

lasantosr avatar

Stargazers

plucky avatar Gautam Korlam avatar ethan avatar Jessica Black avatar Louis Thiery avatar Yam(长琴) avatar Iurii Botylev avatar  avatar

Watchers

Lucian avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.