1#[derive(Copy, Clone, PartialEq, Eq, Debug)]
14pub struct Ipv4(pub [u8; 4]);
15
16impl Ipv4 {
17 pub fn parse(s: &str) -> Option<Ipv4> {
19 let mut out = [0u8; 4];
20 let mut parts = 0usize;
21 for field in s.split('.') {
22 if parts == 4 || field.is_empty() || field.len() > 3 {
23 return None;
24 }
25 let mut v: u16 = 0;
26 for c in field.bytes() {
27 if !c.is_ascii_digit() {
28 return None;
29 }
30 v = v * 10 + (c - b'0') as u16;
31 }
32 if v > 255 {
33 return None;
34 }
35 out[parts] = v as u8;
36 parts += 1;
37 }
38 if parts == 4 { Some(Ipv4(out)) } else { None }
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn parses_ordinary_addresses() {
48 assert_eq!(Ipv4::parse("10.0.2.2"), Some(Ipv4([10, 0, 2, 2])));
49 assert_eq!(Ipv4::parse("0.0.0.0"), Some(Ipv4([0, 0, 0, 0])));
50 assert_eq!(Ipv4::parse("255.255.255.255"), Some(Ipv4([255, 255, 255, 255])));
51 assert_eq!(Ipv4::parse("192.168.4.43"), Some(Ipv4([192, 168, 4, 43])));
52 }
53
54 #[test]
58 fn refuses_what_is_not_an_address() {
59 assert_eq!(Ipv4::parse("256.0.0.1"), None, "an octet past 255");
60 assert_eq!(Ipv4::parse("1.2.3"), None, "three octets");
61 assert_eq!(Ipv4::parse("1.2.3.4.5"), None, "five octets");
62 assert_eq!(Ipv4::parse(""), None);
63 assert_eq!(Ipv4::parse("..."), None, "four empty fields");
64 assert_eq!(Ipv4::parse("1.2.3."), None, "a trailing dot leaves an empty field");
65 assert_eq!(Ipv4::parse(".1.2.3"), None, "and so does a leading one");
66 assert_eq!(Ipv4::parse("1.2.3.x"), None, "a non-digit");
67 assert_eq!(Ipv4::parse("1.2.3.-1"), None, "a sign is not a digit");
68 assert_eq!(Ipv4::parse("1.2.3.4 "), None, "trailing space is not trimmed for us");
69 assert_eq!(Ipv4::parse("1.2.3.0004"), None, "more digits than an octet can hold");
70 }
71
72 #[test]
75 fn octet_length_is_bounded_before_the_value_is() {
76 assert_eq!(Ipv4::parse("1.2.3.0255"), None);
77 assert_eq!(Ipv4::parse("1.2.3.255"), Some(Ipv4([1, 2, 3, 255])));
78 }
79}