output-tracker 0.1.1

Track and assert state of dependencies in state-based tests without mocks
Documentation
use crate::inner_tracker::CelledTracker;
use crate::tracker_handle::TrackerHandle;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::slice;

pub trait CelledSubject<M, T> {
    type Inner<'a>: Deref<Target = BasicSubject<M, T>>
    where
        Self: 'a;
    type InnerMut<'a>: DerefMut<Target = BasicSubject<M, T>>
    where
        Self: 'a;
    type Error: std::error::Error;

    fn subject(&self) -> Result<Self::Inner<'_>, Self::Error>;

    fn subject_mut(&self) -> Result<Self::InnerMut<'_>, Self::Error>;

    fn add_tracker(&self, tracker: T) -> Result<TrackerHandle, Self::Error>
    where
        T: CelledTracker<M>,
    {
        self.subject_mut()
            .map(|mut subject| subject.add_tracker(tracker))
    }

    fn remove_tracker(&self, tracker: TrackerHandle) -> Result<(), Self::Error> {
        self.subject_mut()
            .map(|mut subject| subject.remove_tracker(tracker))
    }

    fn emit(&self, data: M) -> Result<(), Self::Error>
    where
        M: Clone,
        T: CelledTracker<M>,
        Self::Error: From<<T as CelledTracker<M>>::Error>,
    {
        for tracker in self.subject()?.trackers() {
            tracker.track(data.clone())?;
        }
        Ok(())
    }
}

#[derive(Debug)]
pub struct BasicSubject<M, T> {
    _data: PhantomData<M>,
    trackers: Vec<(TrackerHandle, T)>,
}

impl<M, T> Default for BasicSubject<M, T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<M, T> BasicSubject<M, T> {
    pub const fn new() -> Self {
        Self {
            _data: PhantomData,
            trackers: Vec::new(),
        }
    }

    pub fn trackers(&self) -> Trackers<'_, T> {
        Trackers::new(self.trackers.iter())
    }

    pub fn add_tracker(&mut self, tracker: T) -> TrackerHandle {
        let handle = TrackerHandle::new();
        self.trackers.push((handle, tracker));
        handle
    }

    pub fn remove_tracker(&mut self, tracker: TrackerHandle) {
        let found_index = self.trackers.iter().position(|&(it, _)| it == tracker);
        if let Some(idx) = found_index {
            _ = self.trackers.remove(idx);
        }
    }
}

pub struct Trackers<'a, T> {
    inner: slice::Iter<'a, (TrackerHandle, T)>,
}

impl<'a, T> Trackers<'a, T> {
    const fn new(trackers: slice::Iter<'a, (TrackerHandle, T)>) -> Self {
        Self { inner: trackers }
    }
}

impl<'a, T> Iterator for Trackers<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(_, tracker)| tracker)
    }
}