    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93

    use once_cell::sync::Lazy;
    use regex::Regex;
    use serde::Deserialize;

    use crate::{Error, Parse};

    static ADDR_REGEX: Lazy<Regex> = Lazy::new(|| {
        Regex::new(
            r#"(?x)
            \[                     # opening square bracket
            (\s)*                  # optional whitespace
                "(?P<host>[^"]+)"  # host name (string)
                ,                  # separating comma
                (\s)*              # optional whitespace
                (?P<port>\d+)      # port number (integer)
            (\s)*                  # optional whitespace
            \]                     # closing square bracket
        "#,
        )
        .expect("Failed to compile regex: ADDR_REGEX")
    });

    static ADDR_LIST_REGEX: Lazy<Regex> = Lazy::new(|| {
        Regex::new(
            r#"(?x)
            \[                           # opening square bracket (list)
            (\s)*                        # optional whitespace
                (?P<elements>(
                    \[".*", (\s)* \d+\]  # element
                    (,)?                 # element separator
                    (\s)*                # optional whitespace
                )*)
            (\s)*                        # optional whitespace
            \]                           # closing square bracket (list)
        "#,
        )
        .expect("Failed to compile regex: ADDRS_REGEX")
    });

    /// A host/port pair for the server to bind to.
    #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
    pub struct Address {
        /// Host part of address.
        pub host: String,

        /// Port part of address.
        pub port: u16,
    }

    impl Parse for Address {
        fn parse(string: &str) -> Result<Self, Error> {
            let mut items = string
                .trim()
                .trim_start_matches('[')
                .trim_end_matches(']')
                .split(',');

            let parse_error = || Error::ParseAddressError(string.to_string());

            if !ADDR_REGEX.is_match(string) {
                return Err(parse_error());
            }

            Ok(Self {
                host: items.next().ok_or_else(parse_error)?.trim().to_string(),
                port: items.next().ok_or_else(parse_error)?.trim().parse()?,
            })
        }
    }

    impl Parse for Vec<Address> {
        fn parse(string: &str) -> Result<Self, Error> {
            let parse_error = || Error::ParseAddressError(string.to_string());

            if !ADDR_LIST_REGEX.is_match(string) {
                return Err(parse_error());
            }

            let mut addrs = vec![];

            for list_caps in ADDR_LIST_REGEX.captures_iter(string) {
                let elements = &list_caps["elements"].trim();
                for elt_caps in ADDR_REGEX.captures_iter(elements) {
                    addrs.push(Address {
                        host: elt_caps["host"].to_string(),
                        port: elt_caps["port"].parse()?,
                    });
                }
            }

            Ok(addrs)
        }
    }