sofie-values 0.1.0

The values the framework ships: money, email and the like.
Documentation
use std::sync::{Mutex, MutexGuard};

use sofie_core::{Error, Result, unpoisoned};

use super::{Fixture, Seed};

#[derive(Default)]
pub struct Calls {
    state: Mutex<State>,
}

#[derive(Default)]
struct State {
    made: Vec<&'static str>,
    failing: Vec<&'static str>,
    seed: Seed,
}

impl Calls {
    pub fn answer<T: Fixture>(&self, method: &'static str) -> Result<T> {
        let mut state = self.state();
        state.made.push(method);
        if state.failing.contains(&method) {
            return Err(Error::Unavailable(format!("the fake fails `{method}`")));
        }
        Ok(T::fixture(&mut state.seed))
    }

    pub fn value<T: Fixture>(&self, method: &'static str) -> T {
        let mut state = self.state();
        state.made.push(method);
        T::fixture(&mut state.seed)
    }

    pub fn fail(&self, method: &'static str) {
        self.state().failing.push(method);
    }

    #[must_use]
    pub fn made(&self) -> Vec<&'static str> {
        self.state().made.clone()
    }

    fn state(&self) -> MutexGuard<'_, State> {
        unpoisoned::lock(&self.state)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn records_each_call_and_fails_the_methods_it_is_told_to() {
        let calls = Calls::default();

        let answered: Result<i64> = calls.answer("find");
        calls.fail("find");
        let failed: Result<i64> = calls.answer("find");

        assert!(answered.is_ok());
        assert!(matches!(failed, Err(Error::Unavailable(_))));
        assert_eq!(calls.made(), ["find", "find"]);
    }
}