From 1a3f06dc0604ba575ed932bf4c26545a948d0593 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 1 Apr 2022 20:00:17 +0100 Subject: (subplotlib): Ensure that all ContextElement objects implement Debug Signed-off-by: Daniel Silverstone --- subplotlib/helpers/subplotlib_context.rs | 2 +- subplotlib/src/file.rs | 2 +- subplotlib/src/prelude.rs | 6 +++--- subplotlib/src/scenario.rs | 3 ++- subplotlib/src/steplibrary/datadir.rs | 14 ++++++++++++++ subplotlib/src/steplibrary/files.rs | 2 +- subplotlib/src/steplibrary/runcmd.rs | 13 +++++++++++++ subplotlib/subplot-rust-support.rs | 2 +- 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/subplotlib/helpers/subplotlib_context.rs b/subplotlib/helpers/subplotlib_context.rs index c662c98..d5ea7cc 100644 --- a/subplotlib/helpers/subplotlib_context.rs +++ b/subplotlib/helpers/subplotlib_context.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -#[derive(Default)] +#[derive(Debug, Default)] struct Context { counter: usize, files: HashMap, diff --git a/subplotlib/src/file.rs b/subplotlib/src/file.rs index b683c39..84f3495 100644 --- a/subplotlib/src/file.rs +++ b/subplotlib/src/file.rs @@ -18,7 +18,7 @@ use base64::decode; /// /// ```rust /// # use subplotlib::prelude::*; -/// # #[derive(Default)] +/// # #[derive(Debug, Default)] /// # struct Context {} /// # impl ContextElement for Context {} /// #[step] diff --git a/subplotlib/src/prelude.rs b/subplotlib/src/prelude.rs index 4f5b781..e0ac627 100644 --- a/subplotlib/src/prelude.rs +++ b/subplotlib/src/prelude.rs @@ -72,7 +72,7 @@ pub use lazy_static::lazy_static; /// /// ```rust /// # use subplotlib::prelude::*; -/// # #[derive(Default)] struct SomeContextType; +/// # #[derive(Debug, Default)] struct SomeContextType; /// # impl ContextElement for SomeContextType {} /// #[step] /// fn my_step_function(context: &mut SomeContextType, arg1: &str, arg2: &str) @@ -99,8 +99,8 @@ pub use lazy_static::lazy_static; /// /// ```rust /// # use subplotlib::prelude::*; -/// # #[derive(Default)] struct ContextA; -/// # #[derive(Default)] struct ContextB; +/// # #[derive(Debug, Default)] struct ContextA; +/// # #[derive(Debug, Default)] struct ContextB; /// # impl ContextElement for ContextA {} /// # impl ContextElement for ContextB {} /// # impl ContextA { fn get_thingy(&self) -> Result { Ok(0) } } diff --git a/subplotlib/src/scenario.rs b/subplotlib/src/scenario.rs index f382561..9f8e29b 100644 --- a/subplotlib/src/scenario.rs +++ b/subplotlib/src/scenario.rs @@ -4,6 +4,7 @@ //! Instead instances of it are constructed in the generated test functions and //! will be run automatically. +use std::fmt::Debug; use std::{cell::RefCell, marker::PhantomData, sync::Mutex}; use state::Container; @@ -18,7 +19,7 @@ use crate::types::{StepError, StepResult}; /// be aware that they won't always be called on scenario start and they will /// not be caught up the first time they are invoked for a step, simply expected /// to get on with life from their first use. -pub trait ContextElement: Default + Send + 'static { +pub trait ContextElement: Debug + Default + Send + 'static { /// A new context element was created. /// /// In order to permit elements which for example work on disk, this diff --git a/subplotlib/src/steplibrary/datadir.rs b/subplotlib/src/steplibrary/datadir.rs index e6eadbe..5a344df 100644 --- a/subplotlib/src/steplibrary/datadir.rs +++ b/subplotlib/src/steplibrary/datadir.rs @@ -5,6 +5,7 @@ //! If you want to create files, run commands, etc. from your scenarios, you //! will likely be using them from within the datadir. +use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::path::{Component, Path, PathBuf}; @@ -23,10 +24,23 @@ pub struct Datadir { inner: Option, } +#[derive(Debug)] pub struct DatadirInner { base: tempfile::TempDir, } +impl Debug for Datadir { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + Some(inner) => (inner as &dyn Debug).fmt(f), + None => f + .debug_struct("Datadir") + .field("inner", &self.inner) + .finish(), + } + } +} + impl ContextElement for Datadir { fn created(&mut self, scenario: &Scenario) { assert!(self.inner.is_none()); diff --git a/subplotlib/src/steplibrary/files.rs b/subplotlib/src/steplibrary/files.rs index b94155f..991cd88 100644 --- a/subplotlib/src/steplibrary/files.rs +++ b/subplotlib/src/steplibrary/files.rs @@ -18,7 +18,7 @@ pub use crate::prelude::*; pub use super::datadir::Datadir; -#[derive(Default)] +#[derive(Debug, Default)] /// Context data for the `files` step library /// /// This context contains a mapping from filename to metadata so that diff --git a/subplotlib/src/steplibrary/runcmd.rs b/subplotlib/src/steplibrary/runcmd.rs index fc93b7e..b90f8b7 100644 --- a/subplotlib/src/steplibrary/runcmd.rs +++ b/subplotlib/src/steplibrary/runcmd.rs @@ -8,6 +8,7 @@ pub use crate::prelude::*; use std::collections::HashMap; use std::env::{self, JoinPathsError}; use std::ffi::{OsStr, OsString}; +use std::fmt::Debug; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; @@ -27,6 +28,18 @@ pub struct Runcmd { stderr: Vec, } +impl Debug for Runcmd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Runcmd") + .field("env", &self.env) + .field("paths", &self.paths) + .field("exitcode", &self.exitcode) + .field("stdout", &String::from_utf8_lossy(&self.stdout)) + .field("stderr", &String::from_utf8_lossy(&self.stderr)) + .finish() + } +} + // Note, this prefix requires that the injection env vars must have // names which are valid unicode (and ideally ASCII) const ENV_INJECTION_PREFIX: &str = "SUBPLOT_ENV_"; diff --git a/subplotlib/subplot-rust-support.rs b/subplotlib/subplot-rust-support.rs index 60be080..a63ef75 100644 --- a/subplotlib/subplot-rust-support.rs +++ b/subplotlib/subplot-rust-support.rs @@ -7,7 +7,7 @@ use tempfile::TempDir; use std::io::{Read, Seek, SeekFrom}; -#[derive(Default)] +#[derive(Debug, Default)] struct SubplotContext { bin_dir: Option, } -- cgit v1.2.1 From 4f2142453c7d25f581dfce761db9a4003355eeca Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 1 Apr 2022 20:34:25 +0100 Subject: (subplotlib): Make ScenarioContext implement Debug Signed-off-by: Daniel Silverstone --- subplotlib/src/scenario.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/subplotlib/src/scenario.rs b/subplotlib/src/scenario.rs index 9f8e29b..0e25d8b 100644 --- a/subplotlib/src/scenario.rs +++ b/subplotlib/src/scenario.rs @@ -4,6 +4,7 @@ //! Instead instances of it are constructed in the generated test functions and //! will be run automatically. +use core::fmt; use std::fmt::Debug; use std::{cell::RefCell, marker::PhantomData, sync::Mutex}; @@ -123,6 +124,9 @@ trait ScenarioContextHookKind { /// Leave a step fn step_stops(&self, contexts: &ScenarioContext) -> StepResult; + + /// Produce your debug output + fn debug(&self, contexts: &ScenarioContext, dc: &mut DebuggedContext, alternate: bool); } impl ScenarioContextHookKind for ScenarioContextHook @@ -144,6 +148,10 @@ where fn step_stops(&self, contexts: &ScenarioContext) -> StepResult { contexts.with_mut(|c: &mut C| c.step_stops(), true) } + + fn debug(&self, contexts: &ScenarioContext, dc: &mut DebuggedContext, alternate: bool) { + contexts.with_generic(|c: &C| dc.add(c, alternate)); + } } /// A container for all scenario contexts @@ -155,6 +163,54 @@ pub struct ScenarioContext { hooks: RefCell>>, } +#[derive(Default)] +struct DebuggedContext { + body: Vec, +} + +impl DebuggedContext { + fn add(&mut self, obj: &C, alternate: bool) + where + C: Debug, + { + let body = if alternate { + format!("{:#?}", obj) + } else { + format!("{:?}", obj) + }; + self.body.push(body); + } +} + +struct DebugContextString<'a>(&'a str); + +impl<'a> Debug for DebugContextString<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.0) + } +} + +impl Debug for DebuggedContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.body.iter().map(|s| DebugContextString(s))) + .finish() + } +} + +impl Debug for ScenarioContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut contexts = DebuggedContext::default(); + for hook in self.hooks.borrow().iter() { + hook.debug(self, &mut contexts, f.alternate()); + } + f.debug_struct("ScenarioContext") + .field("title", &self.title) + .field("contexts", &contexts) + .finish() + } +} + impl ScenarioContext { fn new(title: &str) -> Self { Self { @@ -187,6 +243,22 @@ impl ScenarioContext { } } + fn with_generic(&self, func: F) + where + F: FnOnce(&C), + C: ContextElement, + { + let sci: &ScenarioContextItem = self + .inner + .try_get() + .expect("Scenario Context item not initialised"); + let lock = match sci.0.lock() { + Ok(lock) => lock, + Err(pe) => pe.into_inner(), + }; + func(&lock) + } + /// With the extracted immutable context, run the function f. pub fn with(&self, func: F, defuse_poison: bool) -> Result where -- cgit v1.2.1