From 20358a9b6b179398a40d4457cf3cafb64493d453 Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Fri, 9 Feb 2024 14:10:12 +0100 Subject: [PATCH] Adjust style guide (loops) and add unreachable macro (#1252) * Adjust style guide (loops) and add unreachable macro * Add macros module to lib.rs * Update docs/STYLE_GUIDE.md Co-authored-by: Malte Kliemann * Move macro to zeitgeist-macros and add examples --------- Co-authored-by: Malte Kliemann --- docs/STYLE_GUIDE.md | 22 +++++++++++++---- macros/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/docs/STYLE_GUIDE.md b/docs/STYLE_GUIDE.md index 085fecad4..4974fb753 100644 --- a/docs/STYLE_GUIDE.md +++ b/docs/STYLE_GUIDE.md @@ -120,13 +120,13 @@ duplicating documentation. - Exceed 70 lines of code per function only in exceptional circumstances. Aim for less. -- No `while` in production. All `for` loops must have a maximum number of - passes. +- Prefer `for` loops over `while` loops. All loops (of any kind) must have a + maximum number of passes. - Use depth checks when using recursion in production. Use recursion only if the algorithm is defined using recursion. - Avoid `mut` in production code if possible without much pain. -- Mark all extrinsics `transactional`, even if they satisfy - the verify-first/write-later principle. +- Mark all extrinsics `transactional`, even if they satisfy the + verify-first/write-later principle. - Avoid indentation over five levels; never go over seven levels. - All public functions must be documented. Documentation of `pub(crate)` and private functions is optional but encouraged. @@ -156,3 +156,17 @@ duplicating documentation. - For larger modules, use one test file per extrinsic for unit tests. Make unit tests as decoupled as possible from other modules. Place end-to-end and integration tests in extra files. +- If possible, test unreachable code and states thought to be impossible using + the following schema: + + ```rust + // In code logic + zeitgeist_macros::unreachable_non_terminating!(condition, log_target, message) + ``` + + ```rust + // In test + #[test] + #[should_panic(expected = message)] + // Cause assertion + ``` diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 783c292e7..2534ff00f 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -32,3 +32,61 @@ macro_rules! create_b_tree_map { [$(($key, $value),)*].iter().cloned().collect::>() } } + +/// This macro does ensure that a condition `$condition` is met, and if it is not met +/// it will log a message `$message` with optional message arguments `message_args` to +/// an optional log target `$log_target`, cause an assertion in a test environment +/// and execute some optional extra code. +/// +/// ```ignore +/// // Examples: +/// unreachable_non_terminating!(a == b, "a does not equal b"); +/// unreachable_non_terminating!(a == b, log_target, "a does not equal b"); +/// unreachable_non_terminating!(a == b, "{:?} != {:?}", a, b); +/// unreachable_non_terminating!(a == b, log_target, "{:?} != {:?}", a, b); +/// ``` +#[macro_export] +macro_rules! unreachable_non_terminating { + ($condition: expr, $message: literal, $($message_args: tt)*) => { + let message = format!($message, $($message_args)*); + + #[cfg(test)] + assert!($condition, "{}", message); + + if !$condition { + log::warn!("{}", message); + } + }; + ($condition: expr, $log_target: ident, $message: literal, $($message_args: tt)*) => { + let message = format!($message, $($message_args)*); + + #[cfg(test)] + assert!($condition, "{}", message); + + if !$condition { + log::warn!(target: $log_target, "{}", message); + } + }; + ($condition: expr, $extra_code: expr, $message: literal, $($message_args: tt)*) => { + let message = format!($message, $($message_args)*); + + #[cfg(test)] + assert!($condition, "{}", message); + + if !$condition { + log::warn!("{}", message); + $extra_code; + } + }; + ($condition: expr, $log_target: ident, $extra_code: expr, $message: literal, $($message_args: tt)*) => { + let message = format!($message, $($message_args)*); + + #[cfg(test)] + assert!($condition, "{}", message); + + if !$condition { + log::warn!(target: $log_target, "{}", message); + $extra_code; + } + }; +}