sync_resolve/
idna.rs

1//! Implements RFC 3490, Internationalized Domain Names in Applications,
2//! encoding for domain name labels containing Unicode.
3
4use std::borrow::Cow::{self, Borrowed, Owned};
5
6use crate::external_idna;
7
8/// Indicates an error in encoding or decoding Punycode data
9#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10pub struct Error;
11
12/// Converts a label or host to its ASCII format. If the string is already
13/// ASCII, it will be returned unmodified. If an error is encountered in
14/// encoding, `Err` will be returned.
15pub fn to_ascii(s: &str) -> Result<Cow<str>, Error> {
16    if s.is_ascii() {
17        Ok(Borrowed(s))
18    } else {
19        external_idna::domain_to_ascii(s)
20            .map(Owned)
21            .map_err(|_| Error)
22    }
23}
24
25/// Converts a label or host to its Unicode format. If the string is not an
26/// internationalized domain name, it will be returned unmodified. If an error
27/// is encountered in decoding, `Err` will be returned.
28pub fn to_unicode(s: &str) -> Result<Cow<str>, Error> {
29    let is_unicode = s.split('.').any(|s| s.starts_with("xn--"));
30
31    if is_unicode {
32        match external_idna::domain_to_unicode(s) {
33            (s, Ok(_)) => Ok(Owned(s)),
34            (_, Err(_)) => Err(Error),
35        }
36    } else {
37        Ok(Borrowed(s))
38    }
39}
40
41#[cfg(test)]
42mod test {
43    use super::{to_ascii, to_unicode};
44
45    static SAMPLE_HOSTS: &'static [(&'static str, &'static str)] = &[
46        ("bücher.de.", "xn--bcher-kva.de."),
47        ("ουτοπία.δπθ.gr.", "xn--kxae4bafwg.xn--pxaix.gr."),
48        // We want to preserve a lack of trailing '.', too.
49        ("bücher.de", "xn--bcher-kva.de"),
50        ("ουτοπία.δπθ.gr", "xn--kxae4bafwg.xn--pxaix.gr"),
51    ];
52
53    #[test]
54    fn test_hosts() {
55        for &(uni, ascii) in SAMPLE_HOSTS {
56            assert_eq!(to_ascii(uni).unwrap(), ascii);
57            assert_eq!(to_unicode(ascii).unwrap(), uni);
58
59            // Ensure the functions are idempotent
60            assert_eq!(to_ascii(ascii).unwrap(), ascii);
61            assert_eq!(to_unicode(uni).unwrap(), uni);
62        }
63    }
64}