#[cfg(feature = "datagen")]
use alloc::string::String;
use icu_provider::prelude::*;
use potential_utf::PotentialUtf8;
use zerovec::ZeroMap;
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_casemap::provider))]
#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
#[yoke(prove_covariance_manually)]
pub struct CaseMapUnfold<'data> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub map: ZeroMap<'data, PotentialUtf8, str>,
}
icu_provider::data_struct!(
CaseMapUnfold<'_>,
#[cfg(feature = "datagen")]
);
impl CaseMapUnfold<'_> {
#[cfg(feature = "datagen")]
#[expect(clippy::indexing_slicing)] pub fn try_from_icu(raw: &[u16]) -> Result<Self, DataError> {
const ROWS_INDEX: usize = 0;
const ROW_WIDTH_INDEX: usize = 1;
const STRING_WIDTH_INDEX: usize = 2;
if raw.len() <= STRING_WIDTH_INDEX {
return Err(DataError::custom("Unfold: header missing"));
}
let num_rows = raw[ROWS_INDEX] as usize;
let row_width = raw[ROW_WIDTH_INDEX] as usize;
let string_width = raw[STRING_WIDTH_INDEX] as usize;
if row_width == 0 {
return Err(DataError::custom("Unfold: invalid row width"));
}
let row_data = &raw[row_width..];
let mut map = ZeroMap::new();
debug_assert!(num_rows == row_data.chunks_exact(row_width).count());
for row in row_data.chunks_exact(row_width) {
let key = Self::decode_string(&row[..string_width])
.ok_or(DataError::custom("Unfold: unpaired surrogate in key"))?;
let val = Self::decode_string(&row[string_width..])
.ok_or(DataError::custom("Unfold: unpaired surrogate in value"))?;
if map
.try_append(PotentialUtf8::from_str(&key), val.as_ref())
.is_some()
{
return Err(DataError::custom("Unfold: keys not sorted/unique"));
}
}
Ok(Self { map })
}
#[cfg(feature = "datagen")]
pub(crate) fn decode_string(slice: &[u16]) -> Option<String> {
let iter = slice.iter().copied().take_while(|&c| c != 0);
char::decode_utf16(iter).collect::<Result<String, _>>().ok()
}
pub(crate) fn get(&self, key: &str) -> Option<&str> {
self.map.get(PotentialUtf8::from_str(key))
}
}