stringprep.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_stringprep_open, icu_stringprep_prepare, UChar, UErrorCode, UStringPrepProfile,
 7    UStringPrepProfileType, USPREP_ALLOW_UNASSIGNED, USPREP_DEFAULT, U_ZERO_ERROR,
 8};
 9use crate::error::Error;
10use crate::Strict;
11use std::ptr::null_mut;
12
13/// Struct representing a given stringprep profile.
14pub(crate) struct Stringprep {
15    inner: *mut UStringPrepProfile,
16}
17
18impl Stringprep {
19    /// Create a new Stringprep struct for the given profile.
20    pub(crate) fn new(profile: UStringPrepProfileType) -> Result<Stringprep, UErrorCode> {
21        let mut err: UErrorCode = U_ZERO_ERROR;
22        let inner = unsafe { icu_stringprep_open(profile, &mut err) };
23        match err {
24            U_ZERO_ERROR => Ok(Stringprep { inner }),
25            err => Err(err),
26        }
27    }
28
29    /// Perform a stringprep operation using this profile.
30    ///
31    /// # Panics
32    /// Panics if ICU doesn’t return a valid UTF-16 string, which should never happen.
33    pub(crate) fn stringprep(&self, input: &str, strict: Strict) -> Result<String, Error> {
34        if input.len() > 1023 {
35            return Err(Error::TooLong);
36        }
37
38        // ICU works on UTF-16 data, so convert it first.
39        let unprepped: Vec<UChar> = input.encode_utf16().collect();
40
41        // Now do the actual stringprep operation.
42        let mut prepped: Vec<UChar> = vec![0u16; 1024];
43        let flags = match strict {
44            Strict::True => USPREP_DEFAULT,
45            Strict::AllowUnassigned => USPREP_ALLOW_UNASSIGNED,
46        };
47        self.prepare(&unprepped, &mut prepped, flags)?;
48
49        // And then convert it back to UTF-8.
50        let output = std::char::decode_utf16(prepped.into_iter())
51            //.map(Result::unwrap)
52            .try_fold(Vec::new(), |mut acc, c| match c {
53                Ok(c) => {
54                    acc.push(c);
55                    Ok(acc)
56                }
57                Err(err) => Err(err),
58            })?;
59        let output: String = output.into_iter().collect();
60
61        if output.len() > 1023 {
62            return Err(Error::TooLong);
63        }
64
65        Ok(output)
66    }
67
68    fn prepare(&self, input: &[UChar], buf: &mut Vec<UChar>, flags: i32) -> Result<(), UErrorCode> {
69        let mut err: UErrorCode = U_ZERO_ERROR;
70        let prepped_len = unsafe {
71            icu_stringprep_prepare(
72                self.inner,
73                input.as_ptr(),
74                input.len() as i32,
75                buf.as_mut_ptr(),
76                buf.len() as i32,
77                flags,
78                null_mut(),
79                &mut err,
80            )
81        };
82        if err != U_ZERO_ERROR {
83            return Err(err);
84        }
85        buf.truncate(prepped_len as usize);
86        Ok(())
87    }
88}