//! Various utility macros /// Create a duration with time suffix `h`, `m`, `s`, `ms` or `ns`. /// /// # Combination /// These can also be combined. /// ``` /// # use flan_utils::duration; /// duration!(1 h, 20 m, 30 s); /// ``` #[macro_export] macro_rules! duration { (0 $($_any:tt)?) => (::core::time::Duration::from_secs(0)); ($dur:literal ms) => (::core::time::Duration::from_millis($dur)); ($dur:literal ns) => (::core::time::Duration::from_nanos($dur)); ($dur:literal s) => (::core::time::Duration::from_secs($dur)); ($dur:literal m) => (::core::time::Duration::from_secs($dur * 60)); ($dur:literal h) => (::core::time::Duration::from_secs($dur * 60 * 60)); ( $($dur:literal $unit:tt),*)=> { duration!(0 s) $( + duration!($dur $unit) )* }; } /// Create a `String` object from a literal. /// /// ``` /// # use flan_utils::string; /// let string = string!("Hello"); /// assert_eq!("Hello", string.as_str()); /// ``` #[cfg(not(feature="no_std"))] #[macro_export] macro_rules! string { ($string:literal) => (::std::string::String::from($string)); } /// A 'soft' assertion of the type `Result<(), impl Error + 'static>`. /// /// # Return type /// The error type created when the condition fails is an opaque ZST which implements `Debug, Clone, Copy, Display, Error`. /// When formatted, it prints the assertion message provided (or "Assertion failed" if none was), as well as a `stringify!` of the failed condition. /// /// It also has two methods: /// ```ignore /// /// The expression that failed as a string. /// #[inline] pub const fn expression(&self) -> &'static str; /// ``` /// ```ignore /// /// The assertion message. /// /// /// /// If one was not provided, this will be "Assertion failed". /// #[inline] pub const fn message(&self) -> &'static str; /// ``` #[cfg(not(feature="no_std"))] #[macro_export] macro_rules! soft_assert { //TODO: Rework, change this back to $(; $message:literal)?, have "Assertion failed" always prepended to the write, and the message conditionally added to the `concat!()` and `self.message()` return the empty string when one was not present. ($cond:expr; $message:literal) => { if !$cond { use ::std::{fmt, error}; /// Opaque type created within macro `soft_assert`. /// /// This type implements `std::{fmt::{Display, Debug}, error::Error}` and is a ZST which implements `Copy`. /// /// It also has two methods: /// ```ignore /// /// The expression that failed as a string. /// #[inline] pub const fn expression(&self) -> &'static str; /// ``` /// ```ignore /// /// The assertion message. /// /// /// /// If one was not provided, this will be "Assertion failed". /// #[inline] pub const fn message(&self) -> &'static str; /// ``` #[derive(Debug, Clone, Copy)] pub struct SoftAssertionFailedError; impl error::Error for SoftAssertionFailedError{} impl fmt::Display for SoftAssertionFailedError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, concat!($message, ": ", stringify!($cond))) } } impl SoftAssertionFailedError { /// The expression that failed as a string. #[allow(dead_code)] #[inline] pub const fn expression(&self) -> &'static str { stringify!($cond) } /// The assertion message. /// /// If one was not provided, this will be "Assertion failed". #[allow(dead_code)] #[inline] pub const fn message(&self) -> &'static str { $message } } Err(SoftAssertionFailedError) } else { Ok(()) } }; ($cond:expr) => { $crate::soft_assert!($cond; "Assertion failed") }; } #[cfg(test)] mod tests { use crate::*; #[test] fn duration() { use core::time::Duration; assert_eq!(duration!(100 ms), Duration::from_millis(100)); assert_eq!(duration!(1 h, 20 m, 30 s), duration!(1 h) + duration!(20 m) + duration!(30 s)); assert_eq!(duration!(3 s, 20 ms, 100 ns), Duration::from_nanos(100) + Duration::from_millis(20) + Duration::from_secs(3)); } #[test] #[cfg(not(feature="no_std"))] fn sort_assert() { let arr = [100; 12]; let ass = soft_assert!(arr.len() == 12); println!("Ass (expect true): {:?}", ass); ass.expect("ass 1 expected true"); let ass = soft_assert!(arr[0] - 50 != 50); println!("Ass (expect false): {:?}", ass); ass.expect_err("ass 1 expected false"); if let Err(err) = soft_assert!(arr[0] - 50 > arr[1]; "Assertion failed: Element was too small") { println!("Ass err `message()`: {}", err.message()); println!("Ass err `expression()`: {}", err.expression()); println!("\n-> {}", err); } else { panic!("ass 3 expected false"); } } #[test] #[cfg(not(feature="no_std"))] fn string() { let string: String = string!("Hello world"); assert_eq!(&string[..], "Hello world"); } }