baktrak 0.1.0

A quick little library for super simple backtracking in Rust.
Documentation
use std::collections::{BTreeMap, BTreeSet, VecDeque};

pub trait BackTrackable: Ord + Clone {
    fn next(&self) -> Vec<Self>
    where
        Self: Sized;
    fn is_final(&self) -> bool;

    fn backtrack(&self) -> Option<Vec<Self>>
    where
        Self: Sized,
    {
        let mut tracker = BackTracker::new(self.clone());
        tracker.run()
    }
}

pub struct BackTracker<T: BackTrackable + Ord + Clone> {
    queue: VecDeque<T>,
    seen: BTreeSet<T>,
    comes_from: BTreeMap<T, T>,
}

impl<T: BackTrackable + Ord + Clone> BackTracker<T> {
    pub fn new(start: T) -> Self {
        Self {
            queue: VecDeque::from_iter(vec![start]),
            seen: BTreeSet::new(),
            comes_from: BTreeMap::new(),
        }
    }

    pub fn run(&mut self) -> Option<Vec<T>> {
        while let Some(current) = self.queue.pop_front() {
            if self.seen.contains(&current) {
                continue;
            }

            if current.is_final() {
                let mut path = vec![current.clone()];
                let mut node = current.clone();
                while let Some(next) = self.comes_from.get(&node) {
                    path.push(next.clone());
                    node = next.clone();
                }
                path.reverse();
                return Some(path);
            }

            current.next().iter().for_each(|next| {
                if !self.seen.contains(next) && !self.queue.contains(next) {
                    self.queue.push_back(next.clone());
                    self.comes_from.insert(next.clone(), current.clone());
                }
            });
            self.seen.insert(current.clone());
        }
        None
    }
}

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

    #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
    struct TestState {
        value: i32,
    }

    impl BackTrackable for TestState {
        fn next(&self) -> Vec<Self> {
            if self.value < 5 {
                vec![TestState {
                    value: self.value + 1,
                }]
            } else {
                vec![]
            }
        }

        fn is_final(&self) -> bool {
            self.value == 5
        }
    }

    #[test]
    fn test_back_tracker() {
        assert_eq!(
            TestState { value: 0 }.backtrack(),
            Some(vec![
                TestState { value: 0 },
                TestState { value: 1 },
                TestState { value: 2 },
                TestState { value: 3 },
                TestState { value: 4 },
                TestState { value: 5 },
            ])
        );
    }
}