eiffel_macros/
lib.rs

1//! # Submodule for Eiffel Inspired Invariant Checking Macros
2//!
3//! This submodule is part of a larger crate that provides features inspired by the Eiffel programming language's
4//! invariant checking. These features include checks for loops, entry, exit, and more.
5//!
6//! This submodule specifically provides the standard macros, as the main crate cannot mix standard macros with
7//! procedural macros due to Cargo's restrictions.
8//!
9//! The Eiffel language's options for invariant checking serve as the basis for the design
10//! and functionality of the macros in this submodule.
11//!
12//! Please note that this submodule, like the main crate, is still a work in progress. As such, some features may not be fully
13//! implemented or may undergo significant changes in future updates.
14//!
15//! Contributions and feedback are always welcome.
16#![deny(warnings)]
17#![deny(missing_docs)]
18
19use thiserror::Error;
20
21/// A simple error type to represent a precondition failed error.
22#[derive(Error, Debug, Clone)]
23#[error("{message}")]
24pub struct PreconditionFailedError {
25    message: &'static str,
26}
27
28#[macro_export]
29/// `require` is a macro that checks if a given condition is met.
30/// If the condition is not met, the macro will cause the program to panic with a specified message.
31/// 
32/// # Arguments
33/// 
34/// * `$condition`: An expression that should evaluate to a boolean. This is the precondition that needs to be checked.
35/// * `$msg`: A message that will be printed if the precondition is not met.
36/// 
37/// # Panics
38/// 
39/// The macro panics if the precondition `$condition` is not met, with a panic message of the form: "Precondition failed: $msg".
40macro_rules! require {
41    ($condition:expr, $msg:expr) => {
42        if !$condition {
43            panic!("Precondition failed: {}", $msg);
44        }
45    };
46} 
47
48#[macro_export]
49/// `require_or_err` is a macro that checks if a given condition is met.
50/// If the condition is not met, the macro will return an error of type `PreconditionFailedError` with a specified message.
51/// 
52/// # Arguments
53/// 
54/// * `$condition`: An expression that should evaluate to a boolean. This is the precondition that needs to be checked.
55/// * `$msg`: A message that will be included in the `PreconditionFailedError` if the precondition is not met.
56/// 
57/// # Errors
58/// 
59/// The macro returns an error of type `PreconditionFailedError` if the precondition `$condition` is not met, with an error message of the form: "Precondition failed: $msg".
60macro_rules! require_or_err {
61    ($condition:expr, $msg:expr) => {
62        if !$condition {
63            return Err($crate::PreconditionFailedError { message: $msg }.into());
64        }
65    };
66}
67
68#[cfg(test)]
69mod tests {
70    ///! Basic usage of the `require!` and `require_or_err!` macros.
71    use super::*;
72
73    fn example_function_with_result(x: i32) -> Result<(), PreconditionFailedError> {
74        require_or_err!(x > 0, "x must be greater than 0");
75
76        // Proceed with the function logic
77        println!("x is a valid argument: {}", x);
78        Ok(())
79    }
80
81    fn example_function_without_result(x: i32) {
82        require!(x > 0, "x must be greater than 0");
83    
84        // Proceed with the function logic
85        println!("x is a valid argument: {}", x);
86    }
87    
88    #[test]
89    fn it_works() {
90        assert!(example_function_with_result(1).is_ok());
91        assert!(example_function_with_result(0).is_err());
92        assert!(example_function_with_result(-1).is_err());
93    }
94
95    #[test]
96    fn it_works_without_result() {
97        example_function_without_result(1);
98    }
99
100    #[test]
101    #[should_panic]
102    fn it_works_without_result_and_panics() {
103        example_function_without_result(0);
104    }
105}