diff options
Diffstat (limited to 'vendor/writeable/src/cmp.rs')
| -rw-r--r-- | vendor/writeable/src/cmp.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/vendor/writeable/src/cmp.rs b/vendor/writeable/src/cmp.rs new file mode 100644 index 00000000..5f0050e2 --- /dev/null +++ b/vendor/writeable/src/cmp.rs @@ -0,0 +1,156 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use crate::Writeable; +use core::cmp::Ordering; +use core::fmt; + +struct WriteComparator<'a> { + code_units: &'a [u8], + result: Ordering, +} + +/// This is an infallible impl. Functions always return Ok, not Err. +impl fmt::Write for WriteComparator<'_> { + #[inline] + fn write_str(&mut self, other: &str) -> fmt::Result { + if self.result != Ordering::Equal { + return Ok(()); + } + let (this, remainder) = self + .code_units + .split_at_checked(other.len()) + .unwrap_or((self.code_units, &[])); + self.code_units = remainder; + self.result = this.cmp(other.as_bytes()); + Ok(()) + } +} + +impl<'a> WriteComparator<'a> { + #[inline] + fn new(code_units: &'a [u8]) -> Self { + Self { + code_units, + result: Ordering::Equal, + } + } + + #[inline] + fn finish(self) -> Ordering { + if matches!(self.result, Ordering::Equal) && !self.code_units.is_empty() { + // Self is longer than Other + Ordering::Greater + } else { + self.result + } + } +} + +/// Compares the contents of a [`Writeable`] to the given UTF-8 bytes without allocating memory. +/// +/// For more details, see: [`cmp_str`] +pub fn cmp_utf8(writeable: &impl Writeable, other: &[u8]) -> Ordering { + let mut wc = WriteComparator::new(other); + let _ = writeable.write_to(&mut wc); + wc.finish().reverse() +} + +/// Compares the contents of a `Writeable` to the given bytes +/// without allocating a String to hold the `Writeable` contents. +/// +/// This returns a lexicographical comparison, the same as if the Writeable +/// were first converted to a String and then compared with `Ord`. For a +/// string ordering suitable for display to end users, use a localized +/// collation crate, such as `icu_collator`. +/// +/// # Examples +/// +/// ``` +/// use core::cmp::Ordering; +/// use core::fmt; +/// use writeable::Writeable; +/// +/// struct WelcomeMessage<'s> { +/// pub name: &'s str, +/// } +/// +/// impl<'s> Writeable for WelcomeMessage<'s> { +/// // see impl in Writeable docs +/// # fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { +/// # sink.write_str("Hello, ")?; +/// # sink.write_str(self.name)?; +/// # sink.write_char('!')?; +/// # Ok(()) +/// # } +/// } +/// +/// let message = WelcomeMessage { name: "Alice" }; +/// let message_str = message.write_to_string(); +/// +/// assert_eq!(Ordering::Equal, writeable::cmp_str(&message, "Hello, Alice!")); +/// +/// assert_eq!(Ordering::Greater, writeable::cmp_str(&message, "Alice!")); +/// assert_eq!(Ordering::Greater, (*message_str).cmp("Alice!")); +/// +/// assert_eq!(Ordering::Less, writeable::cmp_str(&message, "Hello, Bob!")); +/// assert_eq!(Ordering::Less, (*message_str).cmp("Hello, Bob!")); +/// ``` +#[inline] +pub fn cmp_str(writeable: &impl Writeable, other: &str) -> Ordering { + cmp_utf8(writeable, other.as_bytes()) +} + +#[cfg(test)] +mod tests { + use super::*; + use core::fmt::Write; + + mod data { + include!("../tests/data/data.rs"); + } + + #[test] + fn test_write_char() { + for a in data::KEBAB_CASE_STRINGS { + for b in data::KEBAB_CASE_STRINGS { + let mut wc = WriteComparator::new(a.as_bytes()); + for ch in b.chars() { + wc.write_char(ch).unwrap(); + } + assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}"); + } + } + } + + #[test] + fn test_write_str() { + for a in data::KEBAB_CASE_STRINGS { + for b in data::KEBAB_CASE_STRINGS { + let mut wc = WriteComparator::new(a.as_bytes()); + wc.write_str(b).unwrap(); + assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}"); + } + } + } + + #[test] + fn test_mixed() { + for a in data::KEBAB_CASE_STRINGS { + for b in data::KEBAB_CASE_STRINGS { + let mut wc = WriteComparator::new(a.as_bytes()); + let mut first = true; + for substr in b.split('-') { + if first { + first = false; + } else { + wc.write_char('-').unwrap(); + } + wc.write_str(substr).unwrap(); + } + assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}"); + } + } + } +} |
