nhs_number/
from_str.rs

1use std::str::FromStr;
2use crate::NHSNumber;
3use crate::parse_error::ParseError;
4
5/// Implement the `FromStr` trait for NHSNumber to allow parsing from a string.
6///
7/// This parser allows for optional spcae separators in the NHS Number string,
8/// so long as the space separators are in their expected positions.
9///
10impl FromStr for NHSNumber {
11    type Err = ParseError;
12    fn from_str(s: &str) -> Result<Self, Self::Err> {
13        let chars: Vec<char> = s.chars().collect();
14        if chars.len() < 10 || chars.len() > 13 { return Err(ParseError); } // Early exit for invalid length
15        let mut digits: [i8; 10] = [0; 10];
16        let mut i = 0;
17        digits[0] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
18        digits[1] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
19        digits[2] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
20        if chars[i] == ' ' { i += 1; }
21        digits[3] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
22        digits[4] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
23        digits[5] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
24        if chars[i] == ' ' { i += 1; }
25        digits[6] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
26        digits[7] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
27        digits[8] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
28        digits[9] = chars[i].to_digit(10).ok_or(ParseError)? as i8; i += 1;
29        if chars.len() != i { return Err(ParseError); }
30        Ok(NHSNumber { digits: digits })
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn test_from_str_with_spaces() {
40        let s = String::from("012 345 6789");
41        let actual: NHSNumber = NHSNumber::from_str(&s).unwrap();
42        let expect: NHSNumber = NHSNumber::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
43        assert_eq!(actual, expect);
44    }
45
46    #[test]
47    fn test_from_str_without_spaces() {
48        let s = String::from("0123456789");
49        let actual: NHSNumber = NHSNumber::from_str(&s).unwrap();
50        let expect: NHSNumber = NHSNumber::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
51        assert_eq!(actual, expect);
52    }
53
54    #[test]
55    fn test_from_str_with_wrong_characters() {
56        let s = String::from("012-345-6789");
57        let result: Result<NHSNumber, ParseError> = NHSNumber::from_str(&s);
58        assert!(result.is_err());
59    }
60
61    #[test]
62    fn test_from_str_with_wrong_leading_space() {
63        let s = String::from(" 012 345 6789");
64        let result: Result<NHSNumber, ParseError> = NHSNumber::from_str(&s);
65        assert!(result.is_err());
66    }
67
68    #[test]
69    fn test_from_str_with_wrong_inner_space() {
70        let s = String::from("012  345  6789");
71        let result: Result<NHSNumber, ParseError> = NHSNumber::from_str(&s);
72        assert!(result.is_err());
73    }
74
75    #[test]
76    fn test_from_str_with_wrong_trailing_space() {
77        let s = String::from("012 345 6789 ");
78        let result: Result<NHSNumber, ParseError> = NHSNumber::from_str(&s);
79        assert!(result.is_err());
80    }
81
82    #[test]
83    fn test_from_str_with_wrong_length() {
84        let s = String::from("012");
85        let result: Result<NHSNumber, ParseError> = NHSNumber::from_str(&s);
86        assert!(result.is_err());
87    }
88
89}