Skip to main content

whois_rust/
who_is_server_value.rs

1use std::borrow::Cow;
2
3use serde_json::Value;
4use validators::prelude::*;
5
6use crate::{WhoIsError, WhoIsHost};
7
8const DEFAULT_PUNYCODE: bool = true;
9
10/// The model of a WHOIS server.
11#[derive(Debug, Clone)]
12pub struct WhoIsServerValue {
13    pub host:     WhoIsHost,
14    pub query:    Option<String>,
15    pub punycode: bool,
16}
17
18impl WhoIsServerValue {
19    pub fn from_value(value: &Value) -> Result<WhoIsServerValue, WhoIsError> {
20        match value {
21            Value::Object(map) => match map.get("host") {
22                Some(Value::String(host)) => {
23                    let host = match WhoIsHost::parse_str(host) {
24                        Ok(host) => host,
25                        Err(_) => {
26                            return Err(WhoIsError::MapError(
27                                "The server value is an object, but it has not a correct host \
28                                 string.",
29                            ));
30                        },
31                    };
32
33                    let query = match map.get("query") {
34                        Some(query) => {
35                            if let Value::String(query) = query {
36                                Some(String::from(query))
37                            } else {
38                                return Err(WhoIsError::MapError(
39                                    "The server value is an object, but it has an incorrect query \
40                                     string.",
41                                ));
42                            }
43                        },
44                        None => None,
45                    };
46
47                    let punycode = match map.get("punycode") {
48                        Some(punycode) => {
49                            if let Value::Bool(punycode) = punycode {
50                                *punycode
51                            } else {
52                                return Err(WhoIsError::MapError(
53                                    "The server value is an object, but it has an incorrect \
54                                     punycode boolean value.",
55                                ));
56                            }
57                        },
58                        None => DEFAULT_PUNYCODE,
59                    };
60
61                    Ok(WhoIsServerValue {
62                        host,
63                        query,
64                        punycode,
65                    })
66                },
67                _ => Err(WhoIsError::MapError(
68                    "The server value is an object, but it has not a host string.",
69                )),
70            },
71            Value::String(host) => Self::from_string(host),
72            _ => Err(WhoIsError::MapError("The server value is not an object or a host string.")),
73        }
74    }
75
76    #[inline]
77    pub fn from_string<S: AsRef<str>>(string: S) -> Result<WhoIsServerValue, WhoIsError> {
78        let host = string.as_ref();
79
80        let host = match WhoIsHost::parse_str(host) {
81            Ok(host) => host,
82            Err(_) => {
83                return Err(WhoIsError::MapError("The server value is not a correct host string."));
84            },
85        };
86
87        Ok(WhoIsServerValue {
88            host,
89            query: None,
90            punycode: DEFAULT_PUNYCODE,
91        })
92    }
93
94    /// Return the domain text to send to this server. The input is expected to be already ASCII (Punycode) encoded. When `punycode` is `false`, it is decoded back to Unicode, which some servers (e.g. DENIC for `.de`) require.
95    pub(crate) fn encode_domain<'a>(&self, domain: &'a str) -> Cow<'a, str> {
96        if self.punycode {
97            Cow::Borrowed(domain)
98        } else {
99            Cow::Owned(validators::idna::domain_to_unicode(domain).0)
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn encode_domain_honors_punycode_flag() {
110        let mut server = WhoIsServerValue::from_string("whois.denic.de").unwrap();
111
112        // The default keeps the ASCII (Punycode) form.
113        assert_eq!("xn--mller-kva.de", server.encode_domain("xn--mller-kva.de").as_ref());
114
115        // A server with `punycode: false` receives the Unicode form.
116        server.punycode = false;
117        assert_eq!("müller.de", server.encode_domain("xn--mller-kva.de").as_ref());
118    }
119}