knave-expand 0.14.22

The reading, scoping and expansion stages of knave
Documentation
use crate::diagnostic::{Diagnostic, Diagnostics};

#[derive(Clone, Copy)]
pub(super) struct ProblemMark {
    position: usize,
}

impl ProblemMark {
    pub(super) fn end_of(list: &impl ProblemList) -> Self {
        Self {
            position: list.count(),
        }
    }

    pub(super) fn since<L: ProblemList>(self, list: &mut L) -> L {
        list.split_at_mark(self.position)
    }
}

pub(super) trait ProblemList {
    fn count(&self) -> usize;

    fn split_at_mark(&mut self, position: usize) -> Self;
}

impl ProblemList for Vec<Diagnostic> {
    fn count(&self) -> usize {
        self.len()
    }

    fn split_at_mark(&mut self, position: usize) -> Self {
        self.split_off(position)
    }
}

impl ProblemList for Diagnostics {
    fn count(&self) -> usize {
        self.len()
    }

    fn split_at_mark(&mut self, position: usize) -> Self {
        self.split_off(position)
    }
}

#[cfg(test)]
mod tests {
    use super::ProblemMark;
    use crate::diagnostic::{Diagnostic, Diagnostics};
    use crate::expand::problems::ExpandProblems;
    use crate::source::{SourceId, Span};

    struct MarkFixture;

    impl MarkFixture {
        fn problem() -> Diagnostic {
            ExpandProblems::empty_call(Span::new(SourceId::new(0), 0..2))
        }

        fn errors(count: usize) -> Vec<Diagnostic> {
            (0..count).map(|_| Self::problem()).collect()
        }
    }

    #[test]
    fn a_mark_takes_the_errors_added_after_it() {
        let mut errors = MarkFixture::errors(1);
        let mark = ProblemMark::end_of(&errors);
        errors.extend(MarkFixture::errors(2));
        assert_eq!(mark.since(&mut errors).len(), 2);
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn a_mark_with_nothing_after_it_takes_nothing() {
        let mut errors = MarkFixture::errors(2);
        let mark = ProblemMark::end_of(&errors);
        assert!(mark.since(&mut errors).is_empty());
        assert_eq!(errors.len(), 2);
    }

    #[test]
    fn a_mark_takes_the_diagnostics_added_after_it() {
        let mut diagnostics = Diagnostics::default();
        diagnostics.push(MarkFixture::problem());
        let mark = ProblemMark::end_of(&diagnostics);
        diagnostics.push(MarkFixture::problem());
        assert_eq!(mark.since(&mut diagnostics).len(), 1);
        assert_eq!(diagnostics.len(), 1);
    }
}