ciclo 0.1.0

Exhaustive cycle enumeration.
Documentation
use std::fmt::Debug;

#[derive(Debug, PartialEq, Clone)]
pub struct Path<T: PartialEq + Debug> {
    pub bridge: Vec<T>,
    pub target: T,
}

impl<T: PartialEq + Debug> From<Path<T>> for Vec<T> {
    fn from(value: Path<T>) -> Self {
        value
            .bridge
            .into_iter()
            .chain(std::iter::once(value.target))
            .collect()
    }
}

impl<T: PartialEq + Clone + core::fmt::Debug> Path<T> {
    pub fn new(bridge: Vec<T>, target: T) -> Self {
        Self { bridge, target }
    }

    pub fn to(target: T) -> Self {
        Self {
            bridge: Vec::new(),
            target,
        }
    }

    pub fn concat(&self, source: &T, other: &Self) -> Option<Self> {
        if &other.target == source {
            if self.bridge.is_empty() && other.bridge.is_empty() {
                None
            } else {
                self.splice(other)
            }
        } else {
            self.splice(other)
        }
    }

    fn splice(&self, other: &Self) -> Option<Self> {
        let mut bridge = self.bridge.clone();

        bridge.push(self.target.clone());

        for id in other.bridge.iter() {
            if bridge.contains(id) {
                return None;
            } else {
                bridge.push(id.clone())
            }
        }

        Some(Self {
            bridge,
            target: other.target.clone(),
        })
    }
}

#[cfg(test)]
mod concatenate {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn back_edge() {
        let in_path = Path::to(1);
        let out_path = Path::to(0);

        assert_eq!(in_path.concat(&0, &out_path), None)
    }

    #[test]
    fn out_and_back() {
        let in_path = Path::new(vec![1], 2);
        let out_path = Path::new(vec![1], 0);

        assert_eq!(in_path.concat(&0, &out_path), None)
    }

    #[test]
    fn straight() {
        let in_path = Path::new(vec![1], 2);
        let out_path = Path::new(vec![3], 4);

        assert_eq!(
            in_path.concat(&0, &out_path),
            Some(Path {
                bridge: vec![1, 2, 3],
                target: 4
            })
        )
    }

    #[test]
    fn cycle() {
        let in_path = Path::new(vec![1], 2);
        let out_path = Path::to(0);

        assert_eq!(
            in_path.concat(&0, &out_path),
            Some(Path::new(vec![1, 2], 0))
        )
    }
}