| use std::{cmp::Ordering, ops::Deref}; |
| |
| use bstr::{BStr, BString, ByteSlice}; |
| |
| #[cfg_attr(test, derive(Debug))] |
| #[derive(Clone)] |
| pub enum EncodedString { |
| Utf8(String), |
| Unknown(BString), |
| } |
| |
| impl EncodedString { |
| pub fn as_bstr(&self) -> &BStr { |
| match self { |
| EncodedString::Utf8(v) => v.as_str().into(), |
| EncodedString::Unknown(v) => v.as_bstr(), |
| } |
| } |
| pub fn cmp_ref(&self, other: EncodedStringRef<'_>) -> Ordering { |
| match (self, other) { |
| (EncodedString::Utf8(a), EncodedStringRef::Utf8(b)) => { |
| let a = a.chars().map(|c| c.to_ascii_lowercase()); |
| let b = b.chars().map(|c| c.to_ascii_lowercase()); |
| a.cmp(b) |
| } |
| (EncodedString::Unknown(a), EncodedStringRef::Unknown(b)) => a.deref().as_bstr().cmp(b), |
| (EncodedString::Utf8(a), EncodedStringRef::Unknown(b)) => a.as_bytes().cmp(b.as_ref()), |
| (EncodedString::Unknown(a), EncodedStringRef::Utf8(b)) => a.deref().as_bytes().cmp(b.as_bytes()), |
| } |
| } |
| } |
| |
| #[cfg_attr(test, derive(Debug))] |
| #[derive(Clone, Copy)] |
| pub enum EncodedStringRef<'a> { |
| Utf8(&'a str), |
| Unknown(&'a BStr), |
| } |
| |
| impl<'a> From<&'a BStr> for EncodedStringRef<'a> { |
| fn from(v: &'a BStr) -> Self { |
| match v.to_str() { |
| Ok(v) => EncodedStringRef::Utf8(v), |
| Err(_) => EncodedStringRef::Unknown(v), |
| } |
| } |
| } |
| |
| impl<'a> From<EncodedStringRef<'a>> for EncodedString { |
| fn from(v: EncodedStringRef<'a>) -> Self { |
| match v { |
| EncodedStringRef::Utf8(v) => EncodedString::Utf8(v.to_owned()), |
| EncodedStringRef::Unknown(v) => EncodedString::Unknown(v.to_owned()), |
| } |
| } |
| } |
| |
| impl<'a> From<&'a BStr> for EncodedString { |
| fn from(v: &'a BStr) -> Self { |
| match v.to_str() { |
| Ok(v) => EncodedString::Utf8(v.to_owned()), |
| Err(_) => EncodedString::Unknown(v.to_owned()), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod encoded_string { |
| use std::cmp::Ordering; |
| |
| use crate::snapshot::util::{EncodedString, EncodedStringRef}; |
| |
| #[test] |
| fn basic_ascii_case_folding() { |
| assert_eq!( |
| EncodedString::Utf8("FooBar".into()).cmp_ref(EncodedStringRef::Utf8("foobar")), |
| Ordering::Equal |
| ); |
| } |
| |
| #[test] |
| fn no_advanced_unicode_folding() { |
| assert_ne!( |
| EncodedString::Utf8("Masse".into()).cmp_ref(EncodedStringRef::Utf8("Maße")), |
| Ordering::Equal |
| ); |
| } |
| |
| #[test] |
| fn unknown_encoding_pairs_do_not_try_to_ignore_cases() { |
| assert_ne!( |
| EncodedString::Utf8("Foo".into()).cmp_ref(EncodedStringRef::Unknown("foo".into())), |
| Ordering::Equal |
| ); |
| assert_ne!( |
| EncodedString::Unknown("Foo".into()).cmp_ref(EncodedStringRef::Utf8("foo")), |
| Ordering::Equal |
| ); |
| assert_ne!( |
| EncodedString::Unknown("Foo".into()).cmp_ref(EncodedStringRef::Unknown("foo".into())), |
| Ordering::Equal |
| ); |
| } |
| } |