#initialization #structs #experimental #decompose #generate #step #decomposing

macro prismatic

An experimental Rust crate for decomposing the initialization of structs

2 unstable releases

Uses old Rust 2015

0.2.0 Jun 26, 2018
0.1.0 Jun 24, 2018

#1618 in Procedural macros

MIT license

1MB
122 lines

Prismatic

An experimental Rust crate for decomposing the initialization of structs.

prism

Overview

A common idiom in Rust is to define a new function that initializes a struct. Sometimes this function can get quite large. If you try to decompose it into steps, you're often left with complicated signatures on private functions:

impl Webpage {
    pub fn new(title: &str) -> Self {
        let window = Self::fetch_window();
        let document = Self::fetch_document();
        let body = Self::fetch_body(&document);
        let canvas = Self::create_canvas(&document);
        let context = Self::fetch_context(&canvas);

        Self::set_page_title(&document, title);
        Self::reset_styles(&document, &body);
        Self::add_canvas_to_page(&body, &canvas);
        Self::resize_canvas(&body, &canvas);
        Self::bind_resize_event(&window, &body, &canvas);

        Webpage { window, document, body, canvas, context }
    }

    fn fetch_body(document: &Document) -> Body {
      document.body().expect("failed to fetch body")
    }

    fn bind_resize_event(window: &Window, body: &Body, canvas: &canvas) {
      // ...
    }

    // ...
  }

Prismatic

Prismatic provides a macro to make this a bit easier. It generates a special Init struct that can be used for initialization, reducing the overhead of extracting functions for each of the steps:

#[macro_use]
extern crate prismatic;

#[derive(New)]
#[Sig = "title: &str"]
struct Webpage {
  window: Window,
  document: Document,
  body: Body,
  canvas: Canvas,
  context: Context,
}

impl Init {
    fn init(&mut self, title: &str) {
        self.fetch_window();
        self.fetch_document();
        self.fetch_body();
        self.create_canvas();
        self.fetch_context();

        self.set_page_title(title);
        self.reset_styles();
        self.add_canvas_to_page();
        self.resize_canvas();
        self.bind_resize_event();
    }

    fn fetch_body(&mut self) {
        let body = self.document().body().expect("failed to fetch body");
        self.set_body(body);
    }

    fn bind_resize_event(&self) {
      // ...
    }

    // ...
}

In the example above, the fetch_body function uses the the document() getter method and sets the body with the set_body() setter method. Getters and setters are automatically generated for all fields in your struct during initialization.

Usage

Here's a minimal example:

#[macro_use]
extern crate prismatic;

#[derive(New)]
struct Foo { bar: usize }

impl Init {
  fn init(&mut self) {
    self.set_bar(123);
  }
}

let foo = Foo::new()
assert_eq!(foo.bar, 123);

The Sig attribute (short for Signature) can be used if the initializer requires additional arguments:

#[derive(New)]
#[Sig = "x: usize"]
struct Foo { bar: usize }

impl Init {
  fn init(&mut self, x: usize) {
    self.set_bar(x);
  }
}

let foo = Foo::new(123);
assert_eq!(foo.bar, 123);

Dependencies