web_url/parse/pre_path/
port.rs

1use std::str::FromStr;
2
3use crate::parse::Error;
4use crate::parse::Error::InvalidPort;
5
6/// Parses the port from the prefix of `s`.
7///
8/// The string `s` should start with a `:` if there is a port.
9///
10/// Returns `Ok(Some(port), rest_of_s)`.
11/// Returns `Ok(None, rest_of_s)` if `s` does not start with a `:`.
12/// Returns `Err(InvalidPort)` if the port is invalid.
13#[allow(clippy::type_complexity)]
14pub fn parse_port(s: &str) -> Result<(Option<u16>, &str), Error> {
15    if !s.is_empty() && s.as_bytes()[0] == b':' {
16        let s: &str = &s[1..];
17        if let Some(slash) = s.as_bytes().iter().position(|c| *c == b'/') {
18            let port: u16 = u16::from_str(&s[..slash]).map_err(|_| InvalidPort)?;
19            Ok((Some(port), &s[slash..]))
20        } else {
21            let port: u16 = u16::from_str(s).map_err(|_| InvalidPort)?;
22            Ok((Some(port), ""))
23        }
24    } else {
25        Ok((None, s))
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use crate::parse::pre_path::parse_port;
32    use crate::parse::Error;
33    use crate::parse::Error::InvalidPort;
34
35    #[test]
36    #[allow(clippy::type_complexity)]
37    fn fn_extract_port() {
38        let test_cases: &[(&str, Result<(Option<u16>, &str), Error>)] = &[
39            ("", Ok((None, ""))),
40            ("anything", Ok((None, "anything"))),
41            (":invalid", Err(InvalidPort)),
42            (":invalid/", Err(InvalidPort)),
43            (":80", Ok((Some(80), ""))),
44            (":80/", Ok((Some(80), "/"))),
45            (":80?", Err(InvalidPort)),
46            (":80#", Err(InvalidPort)),
47            (":80 ", Err(InvalidPort)),
48        ];
49        for (s, expected) in test_cases {
50            let result: Result<(Option<u16>, &str), Error> = parse_port(s);
51            assert_eq!(result, *expected, "s={}", s);
52        }
53    }
54}