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}