logo
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)
    }
}