xmpp-rs/icu/src/stringprep.rs
Emmanuel Gil Peyrot 4bfd85556d icu: Make Stringprep private and add helper functions
This makes the API easier to use.
2022-09-20 19:15:26 +02:00

88 lines
2.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Crate wrapping what we need from ICUs C API for JIDs.
//!
//! See <http://site.icu-project.org/>
use crate::bindings::{
icu_stringprep_open, icu_stringprep_prepare, UChar, UErrorCode, UStringPrepProfile,
UStringPrepProfileType, USPREP_ALLOW_UNASSIGNED, USPREP_DEFAULT, U_ZERO_ERROR,
};
use crate::error::Error;
use crate::Strict;
use std::ptr::null_mut;
/// Struct representing a given stringprep profile.
pub(crate) struct Stringprep {
inner: *mut UStringPrepProfile,
}
impl Stringprep {
/// Create a new Stringprep struct for the given profile.
pub(crate) fn new(profile: UStringPrepProfileType) -> Result<Stringprep, UErrorCode> {
let mut err: UErrorCode = U_ZERO_ERROR;
let inner = unsafe { icu_stringprep_open(profile, &mut err) };
match err {
U_ZERO_ERROR => Ok(Stringprep { inner }),
err => Err(err),
}
}
/// Perform a stringprep operation using this profile.
///
/// # Panics
/// Panics if ICU doesnt return a valid UTF-16 string, which should never happen.
pub(crate) fn stringprep(&self, input: &str, strict: Strict) -> Result<String, Error> {
if input.len() > 1023 {
return Err(Error::TooLong);
}
// ICU works on UTF-16 data, so convert it first.
let unprepped: Vec<UChar> = input.encode_utf16().collect();
// Now do the actual stringprep operation.
let mut prepped: Vec<UChar> = vec![0u16; 1024];
let flags = match strict {
Strict::True => USPREP_DEFAULT,
Strict::AllowUnassigned => USPREP_ALLOW_UNASSIGNED,
};
self.prepare(&unprepped, &mut prepped, flags)?;
// And then convert it back to UTF-8.
let output = std::char::decode_utf16(prepped.into_iter())
//.map(Result::unwrap)
.try_fold(Vec::new(), |mut acc, c| match c {
Ok(c) => {
acc.push(c);
Ok(acc)
}
Err(err) => Err(err),
})?;
let output: String = output.into_iter().collect();
if output.len() > 1023 {
return Err(Error::TooLong);
}
Ok(output)
}
fn prepare(&self, input: &[UChar], buf: &mut Vec<UChar>, flags: i32) -> Result<(), UErrorCode> {
let mut err: UErrorCode = U_ZERO_ERROR;
let prepped_len = unsafe {
icu_stringprep_prepare(
self.inner,
input.as_ptr(),
input.len() as i32,
buf.as_mut_ptr(),
buf.len() as i32,
flags,
null_mut(),
&mut err,
)
};
if err != U_ZERO_ERROR {
return Err(err);
}
buf.truncate(prepped_len as usize);
Ok(())
}
}