#convert #struct #macro

macro just-convert

Easy conversion of structures

6 releases

0.1.6 Dec 6, 2023
0.1.5 Dec 5, 2023
0.1.3 Nov 29, 2023

#223 in Procedural macros

Download history 2/week @ 2024-02-26 1/week @ 2024-03-11 92/week @ 2024-04-01

93 downloads per month

MIT license

30KB
696 lines

just-convert

Easy conversion of structures

github crates.io docs.rs

Example

#[derive(JustConvert, Default)]
#[convert(from_into(other::Mouse))]
#[convert(from_into(Cat, default))]
struct Dog {
    #[convert(unwrap(from(Cat)))]
    name: String,
    #[convert(skip(from(other::Mouse)))]
    #[convert(map(from(Cat, ". as i64"), into(Cat, ". as u64")))]
    age: i64,

    // inner of Option must be autoconvert
    #[convert(skip(from_into(other::Mouse)))]
    #[convert(map = ".map(Into::into)")]
    error: Option<DogError>,
}

#[derive(JustConvert, Default)]
struct Cat {
    name: Option<String>,
    age: u64,
    // inner of Option must be autoconvert
    error: Option<CatError>,
}

mod other {
    pub struct Mouse {
        pub name: String,
    }
}

Convert both sides at once

Specify the value for from_into as #[convert(from_into(Struct))], instead of specifying from and into separately

#[derive(JustConvert)]
#[convert(from_into(some::B))]
struct A {
    // ...
}

Rename field

#[derive(JustConvert)]
#[convert(from_into(some::B))]
struct A {
    #[convert(rename = user_id)]
    id: String,
}

struct B {
    user_id: String,
}

Execute an arbitrary expression for the conversion

Use the map attribute to specify an arbitrary expression

To access the current field, use the dot character, for e.g., ".get_value()". To access the current structure, use the this construct, e.g. "this.field.get_value()"

#[derive(JustConvert)]
#[convert(from(B))]
struct A {
    // call any method of this field
    #[convert(map = ".to_hex_string()")]
    id: Uuid,
    // casting (or "this.age as i64")
    #[convert(map = ". as i64")]
    age: i64,
    // call any expression
    #[convert(map = "format!(\"id: {}, age: {}\", this.id, this.age)")]
    message: String,
}

struct B {
    id: String,
    age: u64,
}

Auto convert types inside Option or Vec (and Option<Vec> and Vec<Option>)

#[derive(JustConvert)]
#[convert(from(B))]
struct A {
    value: Option<ValueA>,
    items: Vec<ValueA>,
}

struct B {
    value: Option<ValueB>,
    items: Vec<ValueB>,
}

Ignore some fields

Use the skip attribute to ignore convert

#[derive(JustConvert)]
#[convert(from(B))]
struct A {
    id: String,
    #[convert(skip)]
    age: i64,
}

struct B {
    id: String
}

Declare multiple conversions

Use the #[convert(...)] attribute as many times as needed

#[derive(JustConvert)]
#[convert(from(B))]
#[convert(from_into(C))]
struct A {
    id: String,
    #[convert(skip)]
    age: i64,
}

struct B {
    id: String,
}

struct C {
    id: String,
}

Specialize the value of attributes for each transformation

Optionally, you can specify for which transformation the value specified in the attribute applies. For example, to rename a field only for a from' conversion: #[convert(rename(from = new_name))]or for a specific#[convert(rename(from(StructName, new_name)))]`

Rules by which values can be specialized:

  • Boolean values #[convert(wrap)]: #[convert(wrap(from))] and #[convert(wrap(from(StructName)))]
  • Other (with assignment) #[convert(map = "some_expr")]: #[convert(map(from = "some_expr"))] and #[convert(map(from(StructName, "some_expr")))]
#[derive(JustConvert)]
#[convert(from(B))]
#[convert(from_into(C))]
struct A {
    #[convert(rename(from_into(C, c_id)))]
    #[convert(rename(from = user_id))] // or #[convert(rename(from(B, user_id)))]
    id: String,
    #[convert(skip)]
    age: i64,
}

struct B {
    user_id: String,
}

struct C {
    c_id: String,
}

Inspiration

Thanks to the struct-convert and derive-from-ext libraries

Dependencies

~330–780KB
~19K SLoC