laboratory 2.0.0

A simple, expressive unit test framework for Rust
Documentation
use std::collections::HashMap;
use std::{fmt::Display, rc::Rc, cell::RefCell};
use crate::spec::{Spec, SpecContext, SpecOptions};
use crate::suite::{Suite};

pub type State<T> = HashMap<&'static str, T>;

pub struct SuiteContext<T> {
  pub state: Rc<RefCell<State<T>>>,
  pub after_all_hook: Option<Rc<dyn Fn(&mut State<T>) + 'static>>,
  pub after_each_hook: Option<Rc<dyn Fn(&mut State<T>) + 'static>>,
  pub before_all_hook: Option<Rc<dyn Fn(&mut State<T>) + 'static>>,
  pub before_each_hook: Option<Rc<dyn Fn(&mut State<T>) + 'static>>,
  pub specs: Vec<Spec<T>>,
  pub suites: Vec<Suite<T>>,
  pub retries_: Option<u32>,
  pub skip_: bool,
  pub slow_: Option<u128>,
  pub passed: u32,
  pub failed: u32,
  pub ignored: u32,
  pub fail: bool
}
impl<T> SuiteContext<T> {
  pub fn new() -> SuiteContext<T> {
    SuiteContext {
      state: Rc::new(RefCell::new(HashMap::new())),
      after_all_hook: None,
      after_each_hook: None,
      before_all_hook: None,
      before_each_hook: None,
      specs: vec![],
      suites: vec![],
      retries_: None,
      skip_: false,
      slow_: None,
      passed: 0,
      failed: 0,
      ignored: 0,
      fail: false
    }
  }
  pub fn before_all<H: Fn(&mut State<T>) + 'static>(&mut self, hook: H) -> &mut Self {
    self.before_all_hook = Some(Rc::new(hook));
    self
  }
  pub fn before_each<H: Fn(&mut State<T>) + 'static>(&mut self, hook: H) -> &mut Self {
    self.before_each_hook = Some(Rc::new(hook));
    self
  }
  pub fn after_all<H: Fn(&mut State<T>) + 'static>(&mut self, hook: H) -> &mut Self {
    self.after_all_hook = Some(Rc::new(hook));
    self
  }
  pub fn after_each<H: Fn(&mut State<T>) + 'static>(&mut self, hook: H) -> &mut Self {
    self.after_each_hook = Some(Rc::new(hook));
    self
  }
  pub fn it<S, H>(&mut self, name: S, hook: H) -> &mut Self
    where 
      S: Into<String> + Display,
      H: Fn(&mut SpecContext<T>) -> Result<(), String> + 'static
  {
    self.specs.push(Spec::new(name.to_string(), self.state.clone(), Box::new(hook)));
    self
  }
  pub fn it_skip<S, H>(&mut self, name: S, hook: H) -> &mut Self
  where 
    S: Into<String> + Display,
    H: Fn(&mut SpecContext<T>) -> Result<(), String> + 'static
 {
    let mut spec = Spec::new(name.to_string(), self.state.clone(), Box::new(hook));
    spec.skip = true;
    self.specs.push(spec);
    self
  }
  pub fn it_only<S, H>(&mut self, name: S, hook: H) -> &mut Self
  where 
    S: Into<String> + Display,
    H: Fn(&mut SpecContext<T>) -> Result<(), String> + 'static
 {
    let mut spec = Spec::new(name.to_string(), self.state.clone(), Box::new(hook));
    spec.only = true;
    self.specs.push(spec);
    self
  }
  pub fn spec<H>(&mut self, cb: H) -> &mut Self
  where
  H: Fn(&mut SpecOptions<T>)
  {
    let mut options: SpecOptions<T> = SpecOptions::new();
    (cb)(&mut options);
    match options.name {
      Some(name) => match options.hook {
        Some(hook) => {
          let mut spec = Spec::new(name.to_string(), self.state.clone(), hook);
          spec.context.retries_ = options.retries_;
          spec.context.slow_ = options.slow_;
          self.specs.push(spec);
        },
        None => { /* no hook skips the test */}
      },
      None => { /* no name skips the test */ }
    }    
    self
  }
  pub fn describe<S, H>(&mut self, name: S, cb: H) -> &mut Self
  where 
    S: Into<String> + Display,
    H: Fn(&mut SuiteContext<T>) + 'static
 {
    let mut suite = Suite::new(name, cb);
    suite.context.state = self.state.clone();
    // (cb)(&mut suite.context);
    self.suites.push(suite);
    self
  }
  pub fn describe_skip<S, H>(&mut self, name: S, cb: H) -> &mut Self
  where
    S: Into<String> + Display,
    H: Fn(&mut SuiteContext<T>) + 'static
 {
    let mut suite = Suite::new(name, cb);
    suite.context.state = self.state.clone();
    suite.context.skip_ = true;
    // (cb)(&mut suite.context);
    self.suites.push(suite);
    self
  }
  pub fn describe_only<S, H>(&mut self, name: S, cb: H) -> &mut Self
  where 
    S: Into<String> + Display,
    H: Fn(&mut SuiteContext<T>) + 'static
 {
    let mut suite = Suite::new(name, cb);
    suite.context.state = self.state.clone();
    suite.only = true;
    // (cb)(&mut suite.context);
    self.suites.push(suite);
    self
  }
  pub fn describe_import(&mut self, mut suite: Suite<T>) -> &mut Self {
    suite.context.state = self.state.clone();
    self.suites.push(suite);
    self
  }
  pub fn describe_import_skip(&mut self, mut suite: Suite<T>) -> &mut Self {
    suite.context.state = self.state.clone();
    suite.context.skip_ = true;
    self.suites.push(suite);
    self
  }
  pub fn describe_import_only(&mut self, mut suite: Suite<T>) -> &mut Self {
    suite.context.state = self.state.clone();
    suite.only = true;
    self.suites.push(suite);
    self
  }
  pub fn skip(&mut self) -> &mut Self {
    self.skip_ = true;
    self
  }
  pub fn retries(&mut self, count: u32) -> &mut Self {
    self.retries_ = Some(count);
    self
  }
  pub fn slow(&mut self, count: u128) -> &mut Self {
    self.slow_ = Some(count);
    self
  }
}