#macro #language #programming #programming-language #invariants #checking #guard

eiffel

Eiffel is a library for Eiffel language inspired guard clauses with Rust macros

4 releases

0.0.4 Feb 28, 2024
0.0.3 Feb 28, 2024
0.0.2 Feb 28, 2024
0.0.1 Oct 30, 2017

#82 in Procedural macros

Download history 4/week @ 2024-02-19 338/week @ 2024-02-26 34/week @ 2024-03-04 5/week @ 2024-03-11 79/week @ 2024-04-01

88 downloads per month

MIT license

14KB
126 lines

eiffel

Build & Test Build & Test against latest dependencies

This is a personal test project only but works. A similar crate with direct expressions is contracts and of course the original LibHoare.

Please refer to them for production usage.

This crate provides a set of macros inspired by the Eiffel programming language's features for invariant checking. The Eiffel language's options for invariant checking serve as the basis for the design and functionality of the macros in this crate.

Please note that this crate is still a work in progress. As such, some features may not be fully implemented or may undergo significant changes in future updates. Contributions and feedback are always welcome, but the crate alternatives mentioned above are better targets for collaboration I assume.

Usage ?

The Eiffel language is known for very robust design by contract and assertions and I immediately loved that even back in the 90s when I first saw it.

Since Rust has a much better Macro Environment than C etc it was immediately obvious we could extend Rust in this way.

A contrived eiffel example:

  set_second (s: INTEGER)
      -- Set the second from `s'.
    require
      valid_argument_for_second: 0 <= s and s <= 59
    do
      second := s
    ensure
      second_set: second = s
    end

in rust:

use eiffel::contract;

struct UnitBetween1And20 {
  val: u8
}

impl UnitBetween1And20 {
  fn new() -> Self {
    Self { val: 1 }
  }

  fn is_valid_val(&self) -> bool {
    self.val >= 1 && self.val <= 20
  }

  #[contract(is_valid_val)]
  pub fn inc(&mut self) {
    self.val += 1;
  }

  #[contract(is_valid_val)]
  pub fn set(&mut self, new_value: u8) {
    self.val = new_value;
  }
}

let mut a = UnitBetween1And20::new();
a.inc();

// Note: Inside the crate we could still do an unchecked
// direct access.
a.val = 20;

// This would fail with a panic! now:
// 
// a.inc();

Todo

This crate needs to more closely align with Eiffel still. ensure is missing for example and the requirement for a seperate funciton should go away and just allow an in-place expression that returns bool.

It would be great if it could also add additional documentation for the function it marks for the 'contract' to be visible in the docs. Sadly not possible with macros yet it seems.

Development Workflow

  • Install rust
  • Configure stable toolchain
    • rustup update stable && rustup default stable
  • Build project against local machine
    • cargo build --workspace
  • Test
    • cargo test --workspace
    • cargo test --workspace --doc
  • Install cargo watch tool for continuous rebuild etc.
    • cargo install cargo-watch
    • cargo watch --clear -x "build" or cargo watch --clear -x "test --workspace --doc"

Cargo.toml contains the links to the subcrates which are required as Rust cannot have the different macros live in the same crate per default. That is why we re-expose.

Sadly that mean we have to manually update Cargo.toml dependent versions before publishing to main.

I will try to work with release-plz to automate pushing new versions, but I am not happy with it yet.

Dependencies

~315–770KB
~18K SLoC