ctcore 0.2.0

Precision tools for the CTfile family of file formats.
Documentation
use crate::{
    build::{Builder, Error, Target},
    text::Printable,
};

#[derive(Debug, PartialEq)]
pub enum Version {
    V2,
    V3,
}

impl Version {
    pub fn start() -> impl Builder<Product = Version> {
        VersionBuilder::Pad
    }
}

#[derive(Debug, PartialEq)]
enum VersionBuilder {
    Pad,
    V,
    Digit,
    V2(usize),
    V3(usize),
}

impl Builder for VersionBuilder {
    type Product = Version;

    fn push(
        self,
        printable: Printable,
    ) -> Result<Target<Self::Product, Self>, Error> {
        match self {
            Self::Pad => {
                if printable == Printable::Space {
                    Ok(Target::Builder(VersionBuilder::V))
                } else {
                    Err(Error::space())
                }
            }
            Self::V => {
                if printable == Printable::V {
                    Ok(Target::Builder(VersionBuilder::Digit))
                } else {
                    Err(Error::v())
                }
            }
            Self::Digit => match printable {
                Printable::D2 => Ok(Target::Builder(VersionBuilder::V2(0))),
                Printable::D3 => Ok(Target::Builder(VersionBuilder::V3(0))),
                _ => Err(Error::two_or_three()),
            },
            Self::V2(zeroes) => {
                if printable == Printable::D0 {
                    if zeroes == 2 {
                        Ok(Target::Product(Version::V2))
                    } else {
                        Ok(Target::Builder(VersionBuilder::V2(zeroes + 1)))
                    }
                } else {
                    Err(Error::zero())
                }
            }
            Self::V3(zeroes) => {
                if printable == Printable::D0 {
                    if zeroes == 2 {
                        Ok(Target::Product(Version::V3))
                    } else {
                        Ok(Target::Builder(VersionBuilder::V3(zeroes + 1)))
                    }
                } else {
                    Err(Error::zero())
                }
            }
        }
    }

    fn done(self) -> Option<Self::Product> {
        None
    }
}

#[cfg(test)]
mod builder_push {
    use crate::build::Target;
    use pretty_assertions::assert_eq;

    use super::*;

    #[test]
    fn pad_non_space() {
        let builder = VersionBuilder::Pad;

        assert_eq!(builder.push(Printable::A), Err(Error::space()))
    }

    #[test]
    fn pad_space() {
        let builder = VersionBuilder::Pad;

        assert_eq!(
            builder.push(Printable::Space),
            Ok(Target::Builder(VersionBuilder::V))
        )
    }

    #[test]
    fn v_letter() {
        let builder = VersionBuilder::V;

        assert_eq!(builder.push(Printable::A), Err(Error::v()))
    }

    #[test]
    fn v_v() {
        let builder = VersionBuilder::V;

        assert_eq!(
            builder.push(Printable::V),
            Ok(Target::Builder(VersionBuilder::Digit))
        )
    }

    #[test]
    fn digit_two() {
        let builder = VersionBuilder::Digit;

        assert_eq!(
            builder.push(Printable::D2),
            Ok(Target::Builder(VersionBuilder::V2(0)))
        )
    }

    #[test]
    fn digit_three() {
        let builder = VersionBuilder::Digit;

        assert_eq!(
            builder.push(Printable::D3),
            Ok(Target::Builder(VersionBuilder::V3(0)))
        )
    }

    #[test]
    fn digit_letter() {
        let builder = VersionBuilder::Digit;

        assert_eq!(builder.push(Printable::X), Err(Error::two_or_three()))
    }

    #[test]
    fn v2_non_zero() {
        let builder = VersionBuilder::V2(0);

        assert_eq!(
            builder.push(Printable::D1),
            Err(Error::Character(vec![Printable::D0]))
        )
    }

    #[test]
    fn v2_first_zero() {
        let builder = VersionBuilder::V2(0);

        assert_eq!(
            builder.push(Printable::D0),
            Ok(Target::Builder(VersionBuilder::V2(1)))
        )
    }

    #[test]
    fn v2_last_zero() {
        let builder = VersionBuilder::V2(2);

        assert_eq!(
            builder.push(Printable::D0),
            Ok(Target::Product(Version::V2))
        )
    }

    #[test]
    fn v3_non_zero() {
        let builder = VersionBuilder::V3(0);

        assert_eq!(
            builder.push(Printable::D1),
            Err(Error::Character(vec![Printable::D0]))
        )
    }

    #[test]
    fn v3_zero() {
        let builder = VersionBuilder::V3(0);

        assert_eq!(
            builder.push(Printable::D0),
            Ok(Target::Builder(VersionBuilder::V3(1)))
        )
    }

    #[test]
    fn v3_last_zero() {
        let builder = VersionBuilder::V3(2);

        assert_eq!(
            builder.push(Printable::D0),
            Ok(Target::Product(Version::V3))
        )
    }
}

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

    #[test]
    fn not_done() {
        let builder = VersionBuilder::V;

        assert_eq!(builder.done(), None)
    }
}