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}