bank 0.4.0

Various bank related traits and implementation. An abstract bank-transaction parser; easily add your own bank. SEPA RF implementation for easy formatting and checking of RF fields.
use std::str::FromStr;
use std::fmt::{self, Display, Formatter};

pub struct OGM {
    // We actually only need 33.219 bits... But that overflows a u32
    data: u64,
}

impl OGM {
    pub fn calculate_check(&self) -> u8 {
        match self.data % 97 {
            0 => 97,
            x => x as u8,
        }
    }

    /// Creates a Belgian structured message from a seed.
    ///
    /// The seed needs to be maximally 10^10 - 1.
    ///
    /// # Example
    ///
    /// ```
    /// let ogm = bank::scr::OGM::new(201709635u64).unwrap();
    /// assert_eq!(ogm.to_string(), "+++020/1709/63575+++");
    /// ```
    pub fn new(input: u64) -> Result<OGM, &'static str> {
        use num::PrimInt;
        if input >= 10.pow(10) {
            return Err("Seed larger than 10^10-1");
        }
        Ok(OGM {
            data: input
        })
    }
}

impl FromStr for OGM {
    type Err = &'static str;
    fn from_str(input: &str) -> Result<OGM, Self::Err> {
        if input.len() != 3+4+5 + 2+6 {
            return Err("Input has invalid format");
        }
        if ! (input.starts_with("+++") || input.starts_with("***")) {
            return Err("Should start with +++")
        }
        if ! (input.ends_with("+++") || input.ends_with("***")) {
            return Err("Should end with +++")
        }
        let input = &input[3..17];
        let mut data = input[0..3].parse().map_err(|_| "Triple is not numerical")?;
        data *= 10000;

        data += input[4..8].parse::<u64>().map_err(|_| "Quad is not numerical")?;
        data *= 1000;

        data += input[9..12].parse::<u64>().map_err(|_| "Last part is not numerical")?;

        let check: u8 = input[12..14].parse::<u8>().map_err(|_| "Check is not numerical")?;

        let ogm = OGM {
            data: data,
        };

        if ogm.calculate_check() != check {
            return Err("Incorrect control number");
        }

        Ok(ogm)
    }
}

impl Display for OGM {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        let i = format!("{:010}", self.data);
        write!(f, "+++{}/{}/{}{}+++", &i[0..3], &i[3..7], &i[7..], self.calculate_check())
    }
}

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

    #[test]
    fn test_ogm_from_str_to_string() {
        let tests = vec![
            ("+++090/9337/55493+++", 909337554),
        ];

        for (input, output) in tests {
            let OGM {
                data
            } = input.parse().unwrap();
            assert_eq!(data, output);

            let result = OGM {
                data: output
            }.to_string();
            assert_eq!(result, input);
        }
    }
}