#macro #pattern #enums #derive #builder #thing #string

macro boring-derive

Derive macros for some common patterns

2 releases

0.1.1 Jul 4, 2024
0.1.0 Jul 2, 2024

#22 in #thing

31 downloads per month
Used in encrypt-stuff

MIT license

28KB
670 lines

boring-derive

Derive macros for simple implementations of traits.

For example From usually has very trivial implementations.

enum Thing {
  Item1(String),
  Item2(usize),
  Item3(f32),
}

impl From<String> for Thing {
  fn from(value: String) -> Self {
    Thing::Item1(value)
  }
}

impl From<usize> for Thing {
  fn from(value: usize) -> Self {
    Thing::Item2(value)
  }
}

impl From<f32> for Thing {
  fn from(value: f32) -> Self {
    Thing::Item3(value)
  }
}

So instead just:

#[derive(From)]
enum Thing {
  Item1(String),
  Item2(usize),
  Item3(f32),
}

lib.rs:

Derive macros for some common patterns

The currently implemented patterns are:

  • Builder
  • From

Builder

for the Builder macro it generates an impl with methods of the form:

fn field(mut self, value: impl Into<Type>) -> Self {
    self.field = value.into()
    self
}

An example of the generated code for a struct is:

#[derive(Default, Builder)]
struct Example {
    item: String,
    value: usize,
}

// generated impl
impl Example {
    fn item(mut self, value: impl Into<String>) -> Self {
        self.item = value.into();
        self
    }

    fn value(mut self, value: impl Into<usize>) -> Self {
        self.value = value.into();
        self
    }
}

// using the values
fn func() {
    let ex = Example::default()
        .item("something")
        .value(1);
    ...
}

if you want to not include a field in the builder pattern use the skip attribute:

#[derive(Builder)]
struct Example {
    #[builder(skip)]
    item: String,
    value: usize,
}

if you do not want to have the Into use the no_into attribute:

#[derive(Builder)]
struct Example {
    #[builder(no_into)]
    item: String,
    value: usize,
}

if you need to alter the names of the associated methods use prefix and/or rename attributes.

#[derive(Builder)]
#[builder(prefix = "set_")]
struct Example {
    item: String,
    #[builder(rename = "num")]
    value: usize,
}

// will generate
impl Example {
    fn set_item(mut self, ..) -> Self {..}
    fn num(mut self, ..) -> Self {..}
}

The Builder pattern is not defined for enums, unit-like struct, newtypes, and tuple structs

From

For the From derive it implements the trivial From<Type> implementations:

#[derive(From)]
enum Example {
    Empty,
    Number(f32),
    Pair(String, String),
}

// will generate
impl From<()> for Example {
    fn from(value: ()) -> Self {
        Example::Empty
    }
}
impl From<f32> for Example {
    fn from(value: f32) -> Self {
        Example::Number(f32)
    }
}
impl From<(String, String)> for Example {
    fn from(value: (String, String)) -> Self {
        Example::Pair(value.0, value.1)
    }
}

For struct datatypes it uses tuples as the type to convert from:

#[derive(From)]
struct Example {
    item: usize
    value: String
}

// generates
impl From<(usize, String)> for Example {
    fn from(value: (usize, String)) -> Self {
        Example {
            item: value.0,
            value: value.1,
        }
    }
}

If you need to not generate a From implementation use the skip attribute

#[derive(From)]
enum Example {
    #[from(skip)]
    Empty,
    Number(f32),
    Pair(String, String),
}

Dependencies

~245–690KB
~16K SLoC