#proc-macro #define #constraints #generate #boiler #debugging #plate

macro vts

Macro to generate boiler plate to define new types with associated constraints

3 stable releases

1.1.2 Feb 3, 2023
1.0.0 Feb 1, 2023

#611 in Procedural macros

MIT/Apache

12KB
239 lines

VTS: Verified TypeS

Continuous integration

This started as a simple thought about implementing a new type system where we can represent a type with its underlying specification.

type NewType = BaseType
    where constraint;

The NewType is defined as a wrapper around BaseType with the constraint attached to it.

For example once could define the following:

vts::vts!{
    /// String that have been already validated against the constraint that
    /// all the characters of the string are valid hexadecimal digits
    ///
    /// For example:
    ///
    /// * `00`
    /// * `a`
    /// * `0123456789abcdef`
    ///
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    pub type HexaString = String
        where self.chars().all(|c| c.is_ascii_hexdigit());
}

So here we say that the HexaString is a String where all the characters comply with the given constraint : all chars are ASCII hexadecimal characters.

In the context of the Rust implementation, this is only a convenient way to wrap a base type and to add some constraint to the lifting. In essence the generated code will look like the following:

struct HexaString(String);
struct HexaStringConstraintError;

impl HexaString {
    pub fn new(value: String) -> Result<Self, HexaStringConstraintError> {
        let value = Self(value);
        if value.__constraint() {
            Ok(value)
        } else {
            Err(HexaStringConstraintError)
        }
    }

    fn __constraint(&self) -> bool {
        self.chars().all(|c| c.is_ascii_hexdigit())
    }
}

impl std::ops::Deref for HexaString {
    type Target = String;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

Dependencies

~1.5MB
~35K SLoC