From fca15ab8cce1e9faa79f8292bdc3babe4e65a1e0 Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Wed, 7 Feb 2024 13:59:43 +0100 Subject: [PATCH] Adjust style guide (loops) and add unreachable macro --- docs/STYLE_GUIDE.md | 22 +++++++++++--- primitives/src/macros.rs | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 primitives/src/macros.rs diff --git a/docs/STYLE_GUIDE.md b/docs/STYLE_GUIDE.md index 085fecad4..76f561244 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_primitives::unreachable_non_terminating!(condition, log_target, message) +``` + +```rust +// In test +#[test] +#[should_panic(expected = message)] +// Cause assertion +``` diff --git a/primitives/src/macros.rs b/primitives/src/macros.rs new file mode 100644 index 000000000..f4b4c04b4 --- /dev/null +++ b/primitives/src/macros.rs @@ -0,0 +1,66 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +/// 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. +#[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; + } + }; +}