icu_collections/codepointinvliststringlist/
mod.rs1#[cfg(feature = "alloc")]
13use crate::codepointinvlist::CodePointInversionListBuilder;
14use crate::codepointinvlist::{CodePointInversionList, CodePointInversionListULE};
15#[cfg(feature = "alloc")]
16use alloc::string::{String, ToString};
17#[cfg(feature = "alloc")]
18use alloc::vec::Vec;
19use displaydoc::Display;
20use yoke::Yokeable;
21use zerofrom::ZeroFrom;
22use zerovec::{VarZeroSlice, VarZeroVec};
23
24impl core::fmt::Debug for CodePointInversionListAndStringListULE {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let this =
<CodePointInversionListAndStringList as
zerovec::__zerovec_internal_reexport::ZeroFrom<CodePointInversionListAndStringListULE>>::zero_from(self);
<CodePointInversionListAndStringList as
core::fmt::Debug>::fmt(&this, f)
}
}#[zerovec::make_varule(CodePointInversionListAndStringListULE)]
29#[zerovec::skip_derive(Ord)]
30#[zerovec::derive(Debug)]
31#[derive(#[automatically_derived]
impl<'data> ::core::fmt::Debug for CodePointInversionListAndStringList<'data>
{
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"CodePointInversionListAndStringList", "cp_inv_list",
&self.cp_inv_list, "str_list", &&self.str_list)
}
}Debug, #[automatically_derived]
impl<'data> ::core::cmp::Eq for CodePointInversionListAndStringList<'data> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<CodePointInversionList<'data>>;
let _: ::core::cmp::AssertParamIsEq<VarZeroVec<'data, str>>;
}
}Eq, #[automatically_derived]
impl<'data> ::core::cmp::PartialEq for
CodePointInversionListAndStringList<'data> {
#[inline]
fn eq(&self, other: &CodePointInversionListAndStringList<'data>) -> bool {
self.cp_inv_list == other.cp_inv_list &&
self.str_list == other.str_list
}
}PartialEq, #[automatically_derived]
impl<'data> ::core::clone::Clone for
CodePointInversionListAndStringList<'data> {
#[inline]
fn clone(&self) -> CodePointInversionListAndStringList<'data> {
CodePointInversionListAndStringList {
cp_inv_list: ::core::clone::Clone::clone(&self.cp_inv_list),
str_list: ::core::clone::Clone::clone(&self.str_list),
}
}
}Clone, unsafe impl<'yoke> yoke::Yokeable<'yoke> for
CodePointInversionListAndStringList<'static> where {
type Output = CodePointInversionListAndStringList<'yoke>;
#[inline]
fn transform(&'yoke self) -> &'yoke Self::Output {
if false { let _: *const &'yoke Self::Output = &raw const self; }
self
}
#[inline]
fn transform_owned(self) -> Self::Output { self }
#[inline]
unsafe fn make(from: Self::Output) -> Self {
::core::mem::transmute::<Self::Output, Self>(from)
}
#[inline]
fn transform_mut<F>(&'yoke mut self, f: F) where F: 'static +
for<'_yoke> FnOnce(&'_yoke mut Self::Output) {
let y = unsafe { &mut *(self as *mut Self as *mut Self::Output) };
f(y)
}
}Yokeable, impl<'zf, 'zf_inner>
zerofrom::ZeroFrom<'zf, CodePointInversionListAndStringList<'zf_inner>>
for CodePointInversionListAndStringList<'zf> where {
fn zero_from(this: &'zf CodePointInversionListAndStringList<'zf_inner>)
-> Self {
match *this {
CodePointInversionListAndStringList {
cp_inv_list: ref __binding_0, str_list: ref __binding_1 } => {
CodePointInversionListAndStringList {
cp_inv_list: <CodePointInversionList<'zf> as
zerofrom::ZeroFrom<'zf,
CodePointInversionList<'zf_inner>>>::zero_from(__binding_0),
str_list: <VarZeroVec<'zf, str> as
zerofrom::ZeroFrom<'zf,
VarZeroVec<'zf_inner, str>>>::zero_from(__binding_1),
}
}
}
}
}ZeroFrom)]
32#[cfg_attr(not(feature = "alloc"), zerovec::skip_derive(ZeroMapKV, ToOwned))]
33#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
35#[cfg_attr(feature = "serde", zerovec::derive(Serialize, Deserialize, Debug))]
36pub struct CodePointInversionListAndStringList<'data> {
37 #[cfg_attr(feature = "serde", serde(borrow))]
38 #[zerovec::varule(CodePointInversionListULE)]
39 cp_inv_list: CodePointInversionList<'data>,
40 #[cfg_attr(feature = "serde", serde(borrow))]
45 str_list: VarZeroVec<'data, str>,
46}
47
48#[cfg(feature = "databake")]
49impl databake::Bake for CodePointInversionListAndStringList<'_> {
50 fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
51 env.insert("icu_collections");
52 let cp_inv_list = self.cp_inv_list.bake(env);
53 let str_list = self.str_list.bake(env);
54 databake::quote! {
56 icu_collections::codepointinvliststringlist::CodePointInversionListAndStringList::from_parts_unchecked(#cp_inv_list, #str_list)
57 }
58 }
59}
60
61#[cfg(feature = "databake")]
62impl databake::BakeSize for CodePointInversionListAndStringList<'_> {
63 fn borrows_size(&self) -> usize {
64 self.cp_inv_list.borrows_size() + self.str_list.borrows_size()
65 }
66}
67
68impl<'data> CodePointInversionListAndStringList<'data> {
69 pub fn try_from(
72 cp_inv_list: CodePointInversionList<'data>,
73 str_list: VarZeroVec<'data, str>,
74 ) -> Result<Self, InvalidStringList> {
75 {
82 let mut it = str_list.iter();
83 if let Some(mut x) = it.next() {
84 if x.len() == 1 {
85 return Err(InvalidStringList::InvalidStringLength(
86 #[cfg(feature = "alloc")]
87 x.to_string(),
88 ));
89 }
90 for y in it {
91 if x.len() == 1 {
92 return Err(InvalidStringList::InvalidStringLength(
93 #[cfg(feature = "alloc")]
94 x.to_string(),
95 ));
96 } else if x == y {
97 return Err(InvalidStringList::StringListNotUnique(
98 #[cfg(feature = "alloc")]
99 x.to_string(),
100 ));
101 } else if x > y {
102 return Err(InvalidStringList::StringListNotSorted(
103 #[cfg(feature = "alloc")]
104 x.to_string(),
105 #[cfg(feature = "alloc")]
106 y.to_string(),
107 ));
108 }
109
110 x = y;
112 }
113 }
114 }
115
116 Ok(CodePointInversionListAndStringList {
117 cp_inv_list,
118 str_list,
119 })
120 }
121
122 #[doc(hidden)] pub const fn from_parts_unchecked(
124 cp_inv_list: CodePointInversionList<'data>,
125 str_list: VarZeroVec<'data, str>,
126 ) -> Self {
127 CodePointInversionListAndStringList {
128 cp_inv_list,
129 str_list,
130 }
131 }
132
133 pub fn size(&self) -> usize {
137 self.cp_inv_list.size() + self.str_list.len()
138 }
139
140 pub fn has_strings(&self) -> bool {
142 !self.str_list.is_empty()
143 }
144
145 pub fn contains_str(&self, s: &str) -> bool {
167 let mut chars = s.chars();
168 if let Some(first_char) = chars.next() {
169 if chars.next().is_none() {
170 return self.contains(first_char);
171 }
172 }
173 self.str_list.binary_search(s).is_ok()
174 }
175
176 pub fn contains_utf8(&self, s: &[u8]) -> bool {
178 use utf8_iter::Utf8CharsEx;
179 let mut chars = s.chars();
180 if let Some(first_char) = chars.next() {
181 if chars.next().is_none() {
182 return self.contains(first_char);
183 }
184 }
185 self.str_list
186 .binary_search_by(|t| t.as_bytes().cmp(s))
187 .is_ok()
188 }
189
190 pub fn contains32(&self, cp: u32) -> bool {
210 self.cp_inv_list.contains32(cp)
211 }
212
213 pub fn contains(&self, ch: char) -> bool {
233 self.contains32(ch as u32)
234 }
235
236 pub fn code_points(&self) -> &CodePointInversionList<'data> {
238 &self.cp_inv_list
239 }
240
241 pub fn strings(&self) -> &VarZeroSlice<str> {
243 &self.str_list
244 }
245}
246
247#[cfg(feature = "alloc")]
248impl<'a> FromIterator<&'a str> for CodePointInversionListAndStringList<'_> {
250 fn from_iter<I>(it: I) -> Self
251 where
252 I: IntoIterator<Item = &'a str>,
253 {
254 let mut builder = CodePointInversionListBuilder::new();
255 let mut strings = Vec::<&str>::new();
256 for s in it {
257 let mut chars = s.chars();
258 if let Some(first_char) = chars.next() {
259 if chars.next().is_none() {
260 builder.add_char(first_char);
261 continue;
262 }
263 }
264 strings.push(s);
265 }
266
267 strings.sort_unstable();
270 strings.dedup();
271
272 let cp_inv_list = builder.build();
273 let str_list = VarZeroVec::<str>::from(&strings);
274
275 CodePointInversionListAndStringList {
276 cp_inv_list,
277 str_list,
278 }
279 }
280}
281
282#[derive(#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () =
{
impl ::core::fmt::Display for InvalidStringList {
fn fmt(&self, formatter: &mut ::core::fmt::Formatter)
-> ::core::fmt::Result {
#[allow(unused_variables)]
match self {
Self::InvalidStringLength() => {
formatter.write_fmt(format_args!("A string in the string list had an invalid length"))
}
Self::StringListNotUnique() => {
formatter.write_fmt(format_args!("A string in the string list appears more than once"))
}
Self::StringListNotSorted() => {
formatter.write_fmt(format_args!("Two strings in the string list compare to each other opposite of sorted order"))
}
}
}
}
};Display, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::fmt::Debug for InvalidStringList {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
InvalidStringList::InvalidStringLength() =>
"InvalidStringLength",
InvalidStringList::StringListNotUnique() =>
"StringListNotUnique",
InvalidStringList::StringListNotSorted() =>
"StringListNotSorted",
})
}
}Debug)]
284#[allow(clippy::exhaustive_enums)] pub enum InvalidStringList {
286 #[cfg_attr(feature = "alloc", displaydoc("Invalid string length for string: {0}"))]
288 InvalidStringLength(#[cfg(feature = "alloc")] String),
289 #[cfg_attr(feature = "alloc", displaydoc("String list has duplicate: {0}"))]
291 StringListNotUnique(#[cfg(feature = "alloc")] String),
292 #[cfg_attr(
294 feature = "alloc",
295 displaydoc("Strings in string list not in sorted order: ({0}, {1})")
296 )]
297 StringListNotSorted(
298 #[cfg(feature = "alloc")] String,
299 #[cfg(feature = "alloc")] String,
300 ),
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_size_has_strings() {
309 let cp_slice = &[0, 1, 0x7F, 0x80, 0xFFFF, 0x1_0000, 0x10_FFFF, 0x11_0000];
310 let cp_list = CodePointInversionList::try_from_u32_inversion_list_slice(cp_slice).unwrap();
311 let str_slice = &["ascii_max", "bmp_max", "unicode_max", "zero"];
312 let str_list = VarZeroVec::<str>::from(str_slice);
313
314 let cpilsl = CodePointInversionListAndStringList::try_from(cp_list, str_list).unwrap();
315
316 assert!(cpilsl.has_strings());
317 assert_eq!(8, cpilsl.size());
318 }
319
320 #[test]
321 fn test_empty_string_allowed() {
322 let cp_slice = &[0, 1, 0x7F, 0x80, 0xFFFF, 0x1_0000, 0x10_FFFF, 0x11_0000];
323 let cp_list = CodePointInversionList::try_from_u32_inversion_list_slice(cp_slice).unwrap();
324 let str_slice = &["", "ascii_max", "bmp_max", "unicode_max", "zero"];
325 let str_list = VarZeroVec::<str>::from(str_slice);
326
327 let cpilsl = CodePointInversionListAndStringList::try_from(cp_list, str_list).unwrap();
328
329 assert!(cpilsl.has_strings());
330 assert_eq!(9, cpilsl.size());
331 }
332
333 #[test]
334 fn test_invalid_string() {
335 let cp_slice = &[0, 1];
336 let cp_list = CodePointInversionList::try_from_u32_inversion_list_slice(cp_slice).unwrap();
337 let str_slice = &["a"];
338 let str_list = VarZeroVec::<str>::from(str_slice);
339
340 let cpilsl = CodePointInversionListAndStringList::try_from(cp_list, str_list);
341
342 assert!(matches!(
343 cpilsl,
344 Err(InvalidStringList::InvalidStringLength(_))
345 ));
346 }
347
348 #[test]
349 fn test_invalid_string_list_has_duplicate() {
350 let cp_slice = &[0, 1];
351 let cp_list = CodePointInversionList::try_from_u32_inversion_list_slice(cp_slice).unwrap();
352 let str_slice = &["abc", "abc"];
353 let str_list = VarZeroVec::<str>::from(str_slice);
354
355 let cpilsl = CodePointInversionListAndStringList::try_from(cp_list, str_list);
356
357 assert!(matches!(
358 cpilsl,
359 Err(InvalidStringList::StringListNotUnique(_))
360 ));
361 }
362
363 #[test]
364 fn test_invalid_string_list_not_sorted() {
365 let cp_slice = &[0, 1];
366 let cp_list = CodePointInversionList::try_from_u32_inversion_list_slice(cp_slice).unwrap();
367 let str_slice = &["xyz", "abc"];
368 let str_list = VarZeroVec::<str>::from(str_slice);
369
370 let cpilsl = CodePointInversionListAndStringList::try_from(cp_list, str_list);
371
372 assert!(matches!(
373 cpilsl,
374 Err(InvalidStringList::StringListNotSorted(_, _))
375 ));
376 }
377
378 #[test]
379 fn test_from_iter_invariants() {
380 let in_strs_1 = ["a", "abc", "xyz", "abc"];
381 let in_strs_2 = ["xyz", "abc", "a", "abc"];
382
383 let cpilsl_1 = CodePointInversionListAndStringList::from_iter(in_strs_1);
384 let cpilsl_2 = CodePointInversionListAndStringList::from_iter(in_strs_2);
385
386 assert_eq!(cpilsl_1, cpilsl_2);
387
388 assert!(cpilsl_1.has_strings());
389 assert!(cpilsl_1.contains_str("abc"));
390 assert!(cpilsl_1.contains_str("xyz"));
391 assert!(!cpilsl_1.contains_str("def"));
392
393 assert_eq!(1, cpilsl_1.cp_inv_list.size());
394 assert!(cpilsl_1.contains('a'));
395 assert!(!cpilsl_1.contains('0'));
396 assert!(!cpilsl_1.contains('q'));
397
398 assert_eq!(3, cpilsl_1.size());
399 }
400}