idna2008.rs

 1//! Crate wrapping what we need from ICU’s C API for JIDs.
 2//!
 3//! See <http://site.icu-project.org/>
 4
 5use crate::bindings::{
 6    icu_idna_name_to_ascii, icu_idna_name_to_unicode, icu_idna_open, UErrorCode, UIDNAInfo,
 7    UIdnaFunction, UIDNA, U_ZERO_ERROR,
 8};
 9use crate::error::Error;
10
11/// TODO: IDNA2008 support.
12pub struct Idna {
13    inner: *mut UIDNA,
14}
15
16impl Idna {
17    /// Create a new Idna struct.
18    pub fn new(options: u32) -> Result<Idna, UErrorCode> {
19        let mut err: UErrorCode = U_ZERO_ERROR;
20        let inner = unsafe { icu_idna_open(options, &mut err) };
21        match err {
22            U_ZERO_ERROR => Ok(Idna { inner }),
23            err => Err(err),
24        }
25    }
26
27    /// Converts a whole domain name into its ASCII form for DNS lookup.
28    pub fn to_ascii(&self, input: &str) -> Result<String, Error> {
29        self.idna(input, icu_idna_name_to_ascii)
30    }
31
32    /// Converts a whole domain name into its Unicode form for human-readable display.
33    pub fn to_unicode(&self, input: &str) -> Result<String, Error> {
34        self.idna(input, icu_idna_name_to_unicode)
35    }
36
37    fn idna(&self, input: &str, function: UIdnaFunction) -> Result<String, Error> {
38        if input.len() > 255 {
39            return Err(Error::TooLong);
40        }
41
42        let mut err: UErrorCode = U_ZERO_ERROR;
43        let mut dest: Vec<u8> = vec![0u8; 256];
44        let mut info = UIDNAInfo::new();
45        let len = unsafe {
46            function(
47                self.inner,
48                input.as_ptr(),
49                input.len() as i32,
50                dest.as_mut_ptr(),
51                dest.len() as i32,
52                &mut info,
53                &mut err,
54            )
55        };
56        if err != U_ZERO_ERROR {
57            return Err(Error::from_icu_code(err));
58        }
59        let errors = info.get_errors();
60        if errors != 0 {
61            return Err(Error::Idna(errors));
62        }
63        if len > 255 {
64            return Err(Error::TooLong);
65        }
66        dest.truncate(len as usize);
67        Ok(String::from_utf8(dest)?)
68    }
69}