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}