commit c26b45e45228ca6641d7e595f23b4dd8e6cde40b Author: Avril Date: Fri Mar 5 04:39:21 2021 +0000 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80aca69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +*~ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8f39d3c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "flan-utils" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["macros"] + +# #![no_std] support +no_std = [] + +# Enable the macros section +macros = [] + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a935e30 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +//! Collection of helpers / utilities + +#![cfg_attr(feature="no_std", no_std)] + +#[cfg(feature="macros")] #[macro_use] pub mod macros; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..a826cd2 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,166 @@ +//! 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"); + } +}