summaryrefslogtreecommitdiff
path: root/vendor/writeable/src
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
committermo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
commit01959b16a21b22b5df5f16569c2a8e8f92beecef (patch)
tree32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/writeable/src
parentff30574117a996df332e23d1fb6f65259b316b5b (diff)
chore: vendor dependencies
Diffstat (limited to 'vendor/writeable/src')
-rw-r--r--vendor/writeable/src/cmp.rs156
-rw-r--r--vendor/writeable/src/either.rs41
-rw-r--r--vendor/writeable/src/impls.rs262
-rw-r--r--vendor/writeable/src/lib.rs455
-rw-r--r--vendor/writeable/src/ops.rs294
-rw-r--r--vendor/writeable/src/parts_write_adapter.rs115
-rw-r--r--vendor/writeable/src/testing.rs78
-rw-r--r--vendor/writeable/src/to_string_or_borrow.rs210
-rw-r--r--vendor/writeable/src/try_writeable.rs439
9 files changed, 2050 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}");
+ }
+ }
+ }
+}
diff --git a/vendor/writeable/src/either.rs b/vendor/writeable/src/either.rs
new file mode 100644
index 00000000..67be94f5
--- /dev/null
+++ b/vendor/writeable/src/either.rs
@@ -0,0 +1,41 @@
+// 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::*;
+use ::either::Either;
+
+/// A [`Writeable`] impl that delegates to one type or another type.
+impl<W0, W1> Writeable for Either<W0, W1>
+where
+ W0: Writeable,
+ W1: Writeable,
+{
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ match self {
+ Either::Left(w) => w.write_to(sink),
+ Either::Right(w) => w.write_to(sink),
+ }
+ }
+
+ fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
+ match self {
+ Either::Left(w) => w.write_to_parts(sink),
+ Either::Right(w) => w.write_to_parts(sink),
+ }
+ }
+
+ fn writeable_length_hint(&self) -> LengthHint {
+ match self {
+ Either::Left(w) => w.writeable_length_hint(),
+ Either::Right(w) => w.writeable_length_hint(),
+ }
+ }
+
+ fn write_to_string(&self) -> Cow<str> {
+ match self {
+ Either::Left(w) => w.write_to_string(),
+ Either::Right(w) => w.write_to_string(),
+ }
+ }
+}
diff --git a/vendor/writeable/src/impls.rs b/vendor/writeable/src/impls.rs
new file mode 100644
index 00000000..bf935161
--- /dev/null
+++ b/vendor/writeable/src/impls.rs
@@ -0,0 +1,262 @@
+// 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::*;
+use core::fmt;
+
+macro_rules! impl_write_num {
+ ($u:ty, $i:ty, $test:ident) => {
+ impl $crate::Writeable for $u {
+ fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
+ const MAX_LEN: usize = <$u>::MAX.ilog10() as usize + 1;
+ let mut buf = [b'0'; MAX_LEN];
+ let mut n = *self;
+ let mut i = MAX_LEN;
+ #[allow(clippy::indexing_slicing)] // n < 10^i
+ while n != 0 {
+ i -= 1;
+ buf[i] = b'0' + (n % 10) as u8;
+ n /= 10;
+ }
+ if i == MAX_LEN {
+ debug_assert_eq!(*self, 0);
+ i -= 1;
+ }
+ #[allow(clippy::indexing_slicing)] // buf is ASCII
+ let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
+ sink.write_str(s)
+ }
+
+ fn writeable_length_hint(&self) -> $crate::LengthHint {
+ LengthHint::exact(self.checked_ilog10().unwrap_or(0) as usize + 1)
+ }
+ }
+
+ impl $crate::Writeable for $i {
+ fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
+ if self.is_negative() {
+ sink.write_str("-")?;
+ }
+ self.unsigned_abs().write_to(sink)
+ }
+
+ fn writeable_length_hint(&self) -> $crate::LengthHint {
+ $crate::LengthHint::exact(if self.is_negative() { 1 } else { 0 })
+ + self.unsigned_abs().writeable_length_hint()
+ }
+ }
+
+ #[test]
+ fn $test() {
+ use $crate::assert_writeable_eq;
+ assert_writeable_eq!(&(0 as $u), "0");
+ assert_writeable_eq!(&(0 as $i), "0");
+ assert_writeable_eq!(&(-0 as $i), "0");
+ assert_writeable_eq!(&(1 as $u), "1");
+ assert_writeable_eq!(&(1 as $i), "1");
+ assert_writeable_eq!(&(-1 as $i), "-1");
+ assert_writeable_eq!(&(9 as $u), "9");
+ assert_writeable_eq!(&(9 as $i), "9");
+ assert_writeable_eq!(&(-9 as $i), "-9");
+ assert_writeable_eq!(&(10 as $u), "10");
+ assert_writeable_eq!(&(10 as $i), "10");
+ assert_writeable_eq!(&(-10 as $i), "-10");
+ assert_writeable_eq!(&(99 as $u), "99");
+ assert_writeable_eq!(&(99 as $i), "99");
+ assert_writeable_eq!(&(-99 as $i), "-99");
+ assert_writeable_eq!(&(100 as $u), "100");
+ assert_writeable_eq!(&(-100 as $i), "-100");
+ assert_writeable_eq!(&<$u>::MAX, <$u>::MAX.to_string());
+ assert_writeable_eq!(&<$i>::MAX, <$i>::MAX.to_string());
+ assert_writeable_eq!(&<$i>::MIN, <$i>::MIN.to_string());
+
+ use rand::{rngs::SmallRng, Rng, SeedableRng};
+ let mut rng = SmallRng::seed_from_u64(4); // chosen by fair dice roll.
+ // guaranteed to be random.
+ for _ in 0..1000 {
+ let rand = rng.gen::<$u>();
+ assert_writeable_eq!(rand, rand.to_string());
+ }
+ }
+ };
+}
+
+impl_write_num!(u8, i8, test_u8);
+impl_write_num!(u16, i16, test_u16);
+impl_write_num!(u32, i32, test_u32);
+impl_write_num!(u64, i64, test_u64);
+impl_write_num!(u128, i128, test_u128);
+impl_write_num!(usize, isize, test_usize);
+
+impl Writeable for str {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ sink.write_str(self)
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ LengthHint::exact(self.len())
+ }
+
+ /// Returns a borrowed `str`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::borrow::Cow;
+ /// use writeable::Writeable;
+ ///
+ /// let cow = "foo".write_to_string();
+ /// assert!(matches!(cow, Cow::Borrowed(_)));
+ /// ```
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ Cow::Borrowed(self)
+ }
+}
+
+impl Writeable for String {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ sink.write_str(self)
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ LengthHint::exact(self.len())
+ }
+
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ Cow::Borrowed(self)
+ }
+}
+
+impl Writeable for char {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ sink.write_char(*self)
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ LengthHint::exact(self.len_utf8())
+ }
+
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ let mut s = String::with_capacity(self.len_utf8());
+ s.push(*self);
+ Cow::Owned(s)
+ }
+}
+
+impl<T: Writeable + ?Sized> Writeable for &T {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ (*self).write_to(sink)
+ }
+
+ #[inline]
+ fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ (*self).write_to_parts(sink)
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ (*self).writeable_length_hint()
+ }
+
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ (*self).write_to_string()
+ }
+}
+
+macro_rules! impl_write_smart_pointer {
+ ($ty:path, T: $extra_bound:path) => {
+ impl<'a, T: ?Sized + Writeable + $extra_bound> Writeable for $ty {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ core::borrow::Borrow::<T>::borrow(self).write_to(sink)
+ }
+ #[inline]
+ fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ core::borrow::Borrow::<T>::borrow(self).write_to_parts(sink)
+ }
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ core::borrow::Borrow::<T>::borrow(self).writeable_length_hint()
+ }
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ core::borrow::Borrow::<T>::borrow(self).write_to_string()
+ }
+ }
+ };
+ ($ty:path) => {
+ // Add a harmless duplicate Writeable bound
+ impl_write_smart_pointer!($ty, T: Writeable);
+ };
+}
+
+impl_write_smart_pointer!(Cow<'a, T>, T: alloc::borrow::ToOwned);
+impl_write_smart_pointer!(alloc::boxed::Box<T>);
+impl_write_smart_pointer!(alloc::rc::Rc<T>);
+impl_write_smart_pointer!(alloc::sync::Arc<T>);
+
+#[test]
+fn test_string_impls() {
+ fn check_writeable_slice<W: Writeable + core::fmt::Display>(writeables: &[W]) {
+ assert_writeable_eq!(&writeables[0], "");
+ assert_writeable_eq!(&writeables[1], "abc");
+ assert!(matches!(writeables[0].write_to_string(), Cow::Borrowed(_)));
+ assert!(matches!(writeables[1].write_to_string(), Cow::Borrowed(_)));
+ }
+
+ // test str impl
+ let arr: &[&str] = &["", "abc"];
+ check_writeable_slice(arr);
+
+ // test String impl
+ let arr: &[String] = &[String::new(), "abc".to_owned()];
+ check_writeable_slice(arr);
+
+ // test char impl
+ let chars = ['a', 'β', '你', '😀'];
+ for i in 0..chars.len() {
+ let s = String::from(chars[i]);
+ assert_writeable_eq!(&chars[i], s);
+ for j in 0..chars.len() {
+ assert_eq!(
+ crate::cmp_str(&chars[j], &s),
+ chars[j].cmp(&chars[i]),
+ "{:?} vs {:?}",
+ chars[j],
+ chars[i]
+ );
+ }
+ }
+
+ // test Cow impl
+ let arr: &[Cow<str>] = &[Cow::Borrowed(""), Cow::Owned("abc".to_string())];
+ check_writeable_slice(arr);
+
+ // test Box impl
+ let arr: &[Box<str>] = &["".into(), "abc".into()];
+ check_writeable_slice(arr);
+
+ // test Rc impl
+ let arr: &[alloc::rc::Rc<str>] = &["".into(), "abc".into()];
+ check_writeable_slice(arr);
+
+ // test Arc impl
+ let arr: &[alloc::sync::Arc<str>] = &["".into(), "abc".into()];
+ check_writeable_slice(arr);
+
+ // test &T impl
+ let arr: &[&String] = &[&String::new(), &"abc".to_owned()];
+ check_writeable_slice(arr);
+}
diff --git a/vendor/writeable/src/lib.rs b/vendor/writeable/src/lib.rs
new file mode 100644
index 00000000..329e90a4
--- /dev/null
+++ b/vendor/writeable/src/lib.rs
@@ -0,0 +1,455 @@
+// 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 ).
+
+// https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations
+#![cfg_attr(not(any(test, doc)), no_std)]
+#![cfg_attr(
+ not(test),
+ deny(
+ clippy::indexing_slicing,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ clippy::exhaustive_structs,
+ clippy::exhaustive_enums,
+ clippy::trivially_copy_pass_by_ref,
+ missing_debug_implementations,
+ )
+)]
+
+//! `writeable` is a utility crate of the [`ICU4X`] project.
+//!
+//! It includes [`Writeable`], a core trait representing an object that can be written to a
+//! sink implementing `std::fmt::Write`. It is an alternative to `std::fmt::Display` with the
+//! addition of a function indicating the number of bytes to be written.
+//!
+//! `Writeable` improves upon `std::fmt::Display` in two ways:
+//!
+//! 1. More efficient, since the sink can pre-allocate bytes.
+//! 2. Smaller code, since the format machinery can be short-circuited.
+//!
+//! # Examples
+//!
+//! ```
+//! use std::fmt;
+//! use writeable::assert_writeable_eq;
+//! use writeable::LengthHint;
+//! use writeable::Writeable;
+//!
+//! struct WelcomeMessage<'s> {
+//! pub name: &'s str,
+//! }
+//!
+//! impl<'s> Writeable for WelcomeMessage<'s> {
+//! 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(())
+//! }
+//!
+//! fn writeable_length_hint(&self) -> LengthHint {
+//! // "Hello, " + '!' + length of name
+//! LengthHint::exact(8 + self.name.len())
+//! }
+//! }
+//!
+//! let message = WelcomeMessage { name: "Alice" };
+//! assert_writeable_eq!(&message, "Hello, Alice!");
+//!
+//! // Types implementing `Writeable` are recommended to also implement `fmt::Display`.
+//! // This can be simply done by redirecting to the `Writeable` implementation:
+//! writeable::impl_display_with_writeable!(WelcomeMessage<'_>);
+//! assert_eq!(message.to_string(), "Hello, Alice!");
+//! ```
+//!
+//! [`ICU4X`]: ../icu/index.html
+
+extern crate alloc;
+
+mod cmp;
+#[cfg(feature = "either")]
+mod either;
+mod impls;
+mod ops;
+mod parts_write_adapter;
+mod testing;
+mod to_string_or_borrow;
+mod try_writeable;
+
+use alloc::borrow::Cow;
+use alloc::string::String;
+use core::fmt;
+
+pub use cmp::{cmp_str, cmp_utf8};
+pub use to_string_or_borrow::to_string_or_borrow;
+pub use try_writeable::TryWriteable;
+
+/// Helper types for trait impls.
+pub mod adapters {
+ use super::*;
+
+ pub use parts_write_adapter::CoreWriteAsPartsWrite;
+ pub use parts_write_adapter::WithPart;
+ pub use try_writeable::TryWriteableInfallibleAsWriteable;
+ pub use try_writeable::WriteableAsTryWriteableInfallible;
+
+ #[derive(Debug)]
+ #[allow(clippy::exhaustive_structs)] // newtype
+ pub struct LossyWrap<T>(pub T);
+
+ impl<T: TryWriteable> Writeable for LossyWrap<T> {
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ let _ = self.0.try_write_to(sink)?;
+ Ok(())
+ }
+
+ fn writeable_length_hint(&self) -> LengthHint {
+ self.0.writeable_length_hint()
+ }
+ }
+
+ impl<T: TryWriteable> fmt::Display for LossyWrap<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let _ = self.0.try_write_to(f)?;
+ Ok(())
+ }
+ }
+}
+
+#[doc(hidden)] // for testing and macros
+pub mod _internal {
+ pub use super::testing::try_writeable_to_parts_for_test;
+ pub use super::testing::writeable_to_parts_for_test;
+ pub use alloc::string::String;
+}
+
+/// A hint to help consumers of `Writeable` pre-allocate bytes before they call
+/// [`write_to`](Writeable::write_to).
+///
+/// This behaves like `Iterator::size_hint`: it is a tuple where the first element is the
+/// lower bound, and the second element is the upper bound. If the upper bound is `None`
+/// either there is no known upper bound, or the upper bound is larger than `usize`.
+///
+/// `LengthHint` implements std`::ops::{Add, Mul}` and similar traits for easy composition.
+/// During computation, the lower bound will saturate at `usize::MAX`, while the upper
+/// bound will become `None` if `usize::MAX` is exceeded.
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+#[non_exhaustive]
+pub struct LengthHint(pub usize, pub Option<usize>);
+
+impl LengthHint {
+ pub fn undefined() -> Self {
+ Self(0, None)
+ }
+
+ /// `write_to` will use exactly n bytes.
+ pub fn exact(n: usize) -> Self {
+ Self(n, Some(n))
+ }
+
+ /// `write_to` will use at least n bytes.
+ pub fn at_least(n: usize) -> Self {
+ Self(n, None)
+ }
+
+ /// `write_to` will use at most n bytes.
+ pub fn at_most(n: usize) -> Self {
+ Self(0, Some(n))
+ }
+
+ /// `write_to` will use between `n` and `m` bytes.
+ pub fn between(n: usize, m: usize) -> Self {
+ Self(Ord::min(n, m), Some(Ord::max(n, m)))
+ }
+
+ /// Returns a recommendation for the number of bytes to pre-allocate.
+ /// If an upper bound exists, this is used, otherwise the lower bound
+ /// (which might be 0).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use writeable::Writeable;
+ ///
+ /// fn pre_allocate_string(w: &impl Writeable) -> String {
+ /// String::with_capacity(w.writeable_length_hint().capacity())
+ /// }
+ /// ```
+ pub fn capacity(&self) -> usize {
+ self.1.unwrap_or(self.0)
+ }
+
+ /// Returns whether the `LengthHint` indicates that the string is exactly 0 bytes long.
+ pub fn is_zero(&self) -> bool {
+ self.1 == Some(0)
+ }
+}
+
+/// [`Part`]s are used as annotations for formatted strings.
+///
+/// For example, a string like `Alice, Bob` could assign a `NAME` part to the
+/// substrings `Alice` and `Bob`, and a `PUNCTUATION` part to `, `. This allows
+/// for example to apply styling only to names.
+///
+/// `Part` contains two fields, whose usage is left up to the producer of the [`Writeable`].
+/// Conventionally, the `category` field will identify the formatting logic that produces
+/// the string/parts, whereas the `value` field will have semantic meaning. `NAME` and
+/// `PUNCTUATION` could thus be defined as
+/// ```
+/// # use writeable::Part;
+/// const NAME: Part = Part {
+/// category: "userlist",
+/// value: "name",
+/// };
+/// const PUNCTUATION: Part = Part {
+/// category: "userlist",
+/// value: "punctuation",
+/// };
+/// ```
+///
+/// That said, consumers should not usually have to inspect `Part` internals. Instead,
+/// formatters should expose the `Part`s they produces as constants.
+#[derive(Clone, Copy, Debug, PartialEq)]
+#[allow(clippy::exhaustive_structs)] // stable
+pub struct Part {
+ pub category: &'static str,
+ pub value: &'static str,
+}
+
+impl Part {
+ /// A part that should annotate error segments in [`TryWriteable`] output.
+ ///
+ /// For an example, see [`TryWriteable`].
+ pub const ERROR: Part = Part {
+ category: "writeable",
+ value: "error",
+ };
+}
+
+/// A sink that supports annotating parts of the string with [`Part`]s.
+pub trait PartsWrite: fmt::Write {
+ type SubPartsWrite: PartsWrite + ?Sized;
+
+ /// Annotates all strings written by the closure with the given [`Part`].
+ fn with_part(
+ &mut self,
+ part: Part,
+ f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
+ ) -> fmt::Result;
+}
+
+/// `Writeable` is an alternative to `std::fmt::Display` with the addition of a length function.
+pub trait Writeable {
+ /// Writes a string to the given sink. Errors from the sink are bubbled up.
+ /// The default implementation delegates to `write_to_parts`, and discards any
+ /// `Part` annotations.
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ self.write_to_parts(&mut parts_write_adapter::CoreWriteAsPartsWrite(sink))
+ }
+
+ /// Write bytes and `Part` annotations to the given sink. Errors from the
+ /// sink are bubbled up. The default implementation delegates to `write_to`,
+ /// and doesn't produce any `Part` annotations.
+ fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
+ self.write_to(sink)
+ }
+
+ /// Returns a hint for the number of UTF-8 bytes that will be written to the sink.
+ ///
+ /// Override this method if it can be computed quickly.
+ fn writeable_length_hint(&self) -> LengthHint {
+ LengthHint::undefined()
+ }
+
+ /// Creates a new `String` with the data from this `Writeable`. Like `ToString`,
+ /// but smaller and faster.
+ ///
+ /// The default impl allocates an owned `String`. However, if it is possible to return a
+ /// borrowed string, overwrite this method to return a `Cow::Borrowed`.
+ ///
+ /// To remove the `Cow` wrapper, call `.into_owned()` or `.as_str()` as appropriate.
+ ///
+ /// # Examples
+ ///
+ /// Inspect a `Writeable` before writing it to the sink:
+ ///
+ /// ```
+ /// use core::fmt::{Result, Write};
+ /// use writeable::Writeable;
+ ///
+ /// fn write_if_ascii<W, S>(w: &W, sink: &mut S) -> Result
+ /// where
+ /// W: Writeable + ?Sized,
+ /// S: Write + ?Sized,
+ /// {
+ /// let s = w.write_to_string();
+ /// if s.is_ascii() {
+ /// sink.write_str(&s)
+ /// } else {
+ /// Ok(())
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Convert the `Writeable` into a fully owned `String`:
+ ///
+ /// ```
+ /// use writeable::Writeable;
+ ///
+ /// fn make_string(w: &impl Writeable) -> String {
+ /// w.write_to_string().into_owned()
+ /// }
+ /// ```
+ fn write_to_string(&self) -> Cow<str> {
+ let hint = self.writeable_length_hint();
+ if hint.is_zero() {
+ return Cow::Borrowed("");
+ }
+ let mut output = String::with_capacity(hint.capacity());
+ let _ = self.write_to(&mut output);
+ Cow::Owned(output)
+ }
+}
+
+/// Implements [`Display`](core::fmt::Display) for types that implement [`Writeable`].
+///
+/// It's recommended to do this for every [`Writeable`] type, as it will add
+/// support for `core::fmt` features like [`fmt!`](std::fmt),
+/// [`print!`](std::print), [`write!`](std::write), etc.
+///
+/// This macro also adds a concrete `to_string` function. This function will shadow the
+/// standard library `ToString`, using the more efficient writeable-based code path.
+/// To add only `Display`, use the `@display` macro variant.
+#[macro_export]
+macro_rules! impl_display_with_writeable {
+ (@display, $type:ty) => {
+ /// This trait is implemented for compatibility with [`fmt!`](alloc::fmt).
+ /// To create a string, [`Writeable::write_to_string`] is usually more efficient.
+ impl core::fmt::Display for $type {
+ #[inline]
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ $crate::Writeable::write_to(&self, f)
+ }
+ }
+ };
+ ($type:ty) => {
+ $crate::impl_display_with_writeable!(@display, $type);
+ impl $type {
+ /// Converts the given value to a `String`.
+ ///
+ /// Under the hood, this uses an efficient [`Writeable`] implementation.
+ /// However, in order to avoid allocating a string, it is more efficient
+ /// to use [`Writeable`] directly.
+ pub fn to_string(&self) -> $crate::_internal::String {
+ $crate::Writeable::write_to_string(self).into_owned()
+ }
+ }
+ };
+}
+
+/// Testing macros for types implementing [`Writeable`].
+///
+/// Arguments, in order:
+///
+/// 1. The [`Writeable`] under test
+/// 2. The expected string value
+/// 3. [`*_parts_eq`] only: a list of parts (`[(start, end, Part)]`)
+///
+/// Any remaining arguments get passed to `format!`
+///
+/// The macros tests the following:
+///
+/// - Equality of string content
+/// - Equality of parts ([`*_parts_eq`] only)
+/// - Validity of size hint
+///
+/// # Examples
+///
+/// ```
+/// # use writeable::Writeable;
+/// # use writeable::LengthHint;
+/// # use writeable::Part;
+/// # use writeable::assert_writeable_eq;
+/// # use writeable::assert_writeable_parts_eq;
+/// # use std::fmt::{self, Write};
+///
+/// const WORD: Part = Part {
+/// category: "foo",
+/// value: "word",
+/// };
+///
+/// struct Demo;
+/// impl Writeable for Demo {
+/// fn write_to_parts<S: writeable::PartsWrite + ?Sized>(
+/// &self,
+/// sink: &mut S,
+/// ) -> fmt::Result {
+/// sink.with_part(WORD, |w| w.write_str("foo"))
+/// }
+/// fn writeable_length_hint(&self) -> LengthHint {
+/// LengthHint::exact(3)
+/// }
+/// }
+///
+/// writeable::impl_display_with_writeable!(Demo);
+///
+/// assert_writeable_eq!(&Demo, "foo");
+/// assert_writeable_eq!(&Demo, "foo", "Message: {}", "Hello World");
+///
+/// assert_writeable_parts_eq!(&Demo, "foo", [(0, 3, WORD)]);
+/// assert_writeable_parts_eq!(
+/// &Demo,
+/// "foo",
+/// [(0, 3, WORD)],
+/// "Message: {}",
+/// "Hello World"
+/// );
+/// ```
+///
+/// [`*_parts_eq`]: assert_writeable_parts_eq
+#[macro_export]
+macro_rules! assert_writeable_eq {
+ ($actual_writeable:expr, $expected_str:expr $(,)?) => {
+ $crate::assert_writeable_eq!($actual_writeable, $expected_str, "")
+ };
+ ($actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{
+ $crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*);
+ }};
+ (@internal, $actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{
+ let actual_writeable = &$actual_writeable;
+ let (actual_str, actual_parts) = $crate::_internal::writeable_to_parts_for_test(actual_writeable);
+ let actual_len = actual_str.len();
+ assert_eq!(actual_str, $expected_str, $($arg)*);
+ assert_eq!(actual_str, $crate::Writeable::write_to_string(actual_writeable), $($arg)+);
+ let length_hint = $crate::Writeable::writeable_length_hint(actual_writeable);
+ let lower = length_hint.0;
+ assert!(
+ lower <= actual_len,
+ "hint lower bound {lower} larger than actual length {actual_len}: {}",
+ format!($($arg)*),
+ );
+ if let Some(upper) = length_hint.1 {
+ assert!(
+ actual_len <= upper,
+ "hint upper bound {upper} smaller than actual length {actual_len}: {}",
+ format!($($arg)*),
+ );
+ }
+ assert_eq!(actual_writeable.to_string(), $expected_str);
+ actual_parts // return for assert_writeable_parts_eq
+ }};
+}
+
+/// See [`assert_writeable_eq`].
+#[macro_export]
+macro_rules! assert_writeable_parts_eq {
+ ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => {
+ $crate::assert_writeable_parts_eq!($actual_writeable, $expected_str, $expected_parts, "")
+ };
+ ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr, $($arg:tt)+) => {{
+ let actual_parts = $crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*);
+ assert_eq!(actual_parts, $expected_parts, $($arg)+);
+ }};
+}
diff --git a/vendor/writeable/src/ops.rs b/vendor/writeable/src/ops.rs
new file mode 100644
index 00000000..a4c1ffd7
--- /dev/null
+++ b/vendor/writeable/src/ops.rs
@@ -0,0 +1,294 @@
+// 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::LengthHint;
+
+impl core::ops::Add<LengthHint> for LengthHint {
+ type Output = Self;
+
+ fn add(self, other: LengthHint) -> Self {
+ LengthHint(
+ self.0.saturating_add(other.0),
+ match (self.1, other.1) {
+ (Some(c), Some(d)) => c.checked_add(d),
+ _ => None,
+ },
+ )
+ }
+}
+
+impl core::ops::AddAssign<LengthHint> for LengthHint {
+ fn add_assign(&mut self, other: Self) {
+ *self = *self + other;
+ }
+}
+
+impl core::iter::Sum<LengthHint> for LengthHint {
+ fn sum<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = LengthHint>,
+ {
+ iter.fold(LengthHint::exact(0), core::ops::Add::add)
+ }
+}
+
+impl core::ops::Add<usize> for LengthHint {
+ type Output = Self;
+
+ fn add(self, other: usize) -> Self {
+ Self(
+ self.0.saturating_add(other),
+ self.1.and_then(|upper| upper.checked_add(other)),
+ )
+ }
+}
+
+impl core::ops::AddAssign<usize> for LengthHint {
+ fn add_assign(&mut self, other: usize) {
+ *self = *self + other;
+ }
+}
+
+impl core::ops::Mul<usize> for LengthHint {
+ type Output = Self;
+
+ fn mul(self, other: usize) -> Self {
+ Self(
+ self.0.saturating_mul(other),
+ self.1.and_then(|upper| upper.checked_mul(other)),
+ )
+ }
+}
+
+impl core::ops::MulAssign<usize> for LengthHint {
+ fn mul_assign(&mut self, other: usize) {
+ *self = *self * other;
+ }
+}
+
+impl core::ops::BitOr<LengthHint> for LengthHint {
+ type Output = Self;
+
+ /// Returns a new hint that is correct wherever `self` is correct, and wherever
+ /// `other` is correct.
+ ///
+ /// Example:
+ /// ```
+ /// # use writeable::{LengthHint, Writeable};
+ /// # use core::fmt;
+ /// # fn coin_flip() -> bool { true }
+ ///
+ /// struct NonDeterministicWriteable(String, String);
+ ///
+ /// impl Writeable for NonDeterministicWriteable {
+ /// fn write_to<W: fmt::Write + ?Sized>(
+ /// &self,
+ /// sink: &mut W,
+ /// ) -> fmt::Result {
+ /// sink.write_str(if coin_flip() { &self.0 } else { &self.1 })
+ /// }
+ ///
+ /// fn writeable_length_hint(&self) -> LengthHint {
+ /// LengthHint::exact(self.0.len()) | LengthHint::exact(self.1.len())
+ /// }
+ /// }
+ ///
+ /// writeable::impl_display_with_writeable!(NonDeterministicWriteable);
+ /// ```
+ fn bitor(self, other: LengthHint) -> Self {
+ LengthHint(
+ Ord::min(self.0, other.0),
+ match (self.1, other.1) {
+ (Some(c), Some(d)) => Some(Ord::max(c, d)),
+ _ => None,
+ },
+ )
+ }
+}
+
+impl core::ops::BitOrAssign<LengthHint> for LengthHint {
+ fn bitor_assign(&mut self, other: Self) {
+ *self = *self | other;
+ }
+}
+
+impl core::iter::Sum<usize> for LengthHint {
+ fn sum<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = usize>,
+ {
+ LengthHint::exact(iter.sum::<usize>())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_add() {
+ assert_eq!(LengthHint::exact(3) + 2, LengthHint::exact(5));
+ assert_eq!(
+ LengthHint::exact(3) + LengthHint::exact(2),
+ LengthHint::exact(5)
+ );
+ assert_eq!(
+ LengthHint::exact(3) + LengthHint::undefined(),
+ LengthHint::at_least(3)
+ );
+
+ assert_eq!(LengthHint::undefined() + 2, LengthHint::at_least(2));
+ assert_eq!(
+ LengthHint::undefined() + LengthHint::exact(2),
+ LengthHint::at_least(2)
+ );
+ assert_eq!(
+ LengthHint::undefined() + LengthHint::undefined(),
+ LengthHint::undefined()
+ );
+
+ assert_eq!(
+ LengthHint::at_least(15) + LengthHint::exact(3),
+ LengthHint::at_least(18)
+ );
+
+ assert_eq!(
+ LengthHint::at_least(15) + LengthHint::at_most(3),
+ LengthHint::at_least(15)
+ );
+
+ assert_eq!(LengthHint::between(48, 92) + 5, LengthHint::between(53, 97));
+
+ let mut len = LengthHint::exact(5);
+ len += LengthHint::exact(3);
+ assert_eq!(len, LengthHint::exact(8));
+ len += 2;
+ assert_eq!(len, LengthHint::exact(10));
+ len += LengthHint::undefined();
+ assert_eq!(len, LengthHint::at_least(10));
+
+ len += LengthHint::exact(3);
+ assert_eq!(len, LengthHint::at_least(13));
+ len += 2;
+ assert_eq!(len, LengthHint::at_least(15));
+ len += LengthHint::undefined();
+ assert_eq!(len, LengthHint::at_least(15));
+
+ assert_eq!(
+ LengthHint::between(usize::MAX - 10, usize::MAX - 5) + LengthHint::exact(20),
+ LengthHint::at_least(usize::MAX)
+ );
+ }
+
+ #[test]
+ fn test_sum() {
+ let lens = [
+ LengthHint::exact(4),
+ LengthHint::exact(1),
+ LengthHint::exact(1),
+ ];
+ assert_eq!(
+ lens.iter().copied().sum::<LengthHint>(),
+ LengthHint::exact(6)
+ );
+
+ let lens = [
+ LengthHint::exact(4),
+ LengthHint::undefined(),
+ LengthHint::at_least(1),
+ ];
+ assert_eq!(
+ lens.iter().copied().sum::<LengthHint>(),
+ LengthHint::at_least(5)
+ );
+
+ let lens = [
+ LengthHint::exact(4),
+ LengthHint::undefined(),
+ LengthHint::at_most(1),
+ ];
+ assert_eq!(
+ lens.iter().copied().sum::<LengthHint>(),
+ LengthHint::at_least(4)
+ );
+
+ let lens = [4, 1, 1];
+ assert_eq!(
+ lens.iter().copied().sum::<LengthHint>(),
+ LengthHint::exact(6)
+ );
+ }
+
+ #[test]
+ fn test_mul() {
+ assert_eq!(LengthHint::exact(3) * 2, LengthHint::exact(6));
+
+ assert_eq!(LengthHint::undefined() * 2, LengthHint::undefined());
+
+ assert_eq!(
+ LengthHint::between(48, 92) * 2,
+ LengthHint::between(96, 184)
+ );
+
+ let mut len = LengthHint::exact(5);
+ len *= 2;
+ assert_eq!(len, LengthHint::exact(10));
+
+ assert_eq!(
+ LengthHint::between(usize::MAX - 10, usize::MAX - 5) * 2,
+ LengthHint::at_least(usize::MAX)
+ );
+ }
+
+ #[test]
+ fn test_bitor() {
+ assert_eq!(
+ LengthHint::exact(3) | LengthHint::exact(2),
+ LengthHint::between(2, 3)
+ );
+ assert_eq!(
+ LengthHint::exact(3) | LengthHint::undefined(),
+ LengthHint::undefined()
+ );
+
+ assert_eq!(
+ LengthHint::undefined() | LengthHint::undefined(),
+ LengthHint::undefined()
+ );
+
+ assert_eq!(
+ LengthHint::exact(10) | LengthHint::exact(10),
+ LengthHint::exact(10)
+ );
+
+ assert_eq!(
+ LengthHint::at_least(15) | LengthHint::exact(3),
+ LengthHint::at_least(3)
+ );
+
+ assert_eq!(
+ LengthHint::at_least(15) | LengthHint::at_most(18),
+ LengthHint::undefined()
+ );
+
+ assert_eq!(
+ LengthHint::at_least(15) | LengthHint::at_least(18),
+ LengthHint::at_least(15)
+ );
+
+ assert_eq!(
+ LengthHint::at_most(15) | LengthHint::at_most(18),
+ LengthHint::at_most(18)
+ );
+
+ assert_eq!(
+ LengthHint::between(5, 10) | LengthHint::at_most(3),
+ LengthHint::at_most(10)
+ );
+
+ let mut len = LengthHint::exact(5);
+ len |= LengthHint::exact(3);
+ assert_eq!(len, LengthHint::between(5, 3));
+ }
+}
diff --git a/vendor/writeable/src/parts_write_adapter.rs b/vendor/writeable/src/parts_write_adapter.rs
new file mode 100644
index 00000000..eacf3a2c
--- /dev/null
+++ b/vendor/writeable/src/parts_write_adapter.rs
@@ -0,0 +1,115 @@
+// 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::*;
+
+/// A wrapper around a type implementing [`fmt::Write`] that implements [`PartsWrite`].
+#[derive(Debug)]
+#[allow(clippy::exhaustive_structs)] // newtype
+pub struct CoreWriteAsPartsWrite<W: fmt::Write + ?Sized>(pub W);
+
+impl<W: fmt::Write + ?Sized> fmt::Write for CoreWriteAsPartsWrite<W> {
+ #[inline]
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.0.write_str(s)
+ }
+
+ #[inline]
+ fn write_char(&mut self, c: char) -> fmt::Result {
+ self.0.write_char(c)
+ }
+}
+
+impl<W: fmt::Write + ?Sized> PartsWrite for CoreWriteAsPartsWrite<W> {
+ type SubPartsWrite = CoreWriteAsPartsWrite<W>;
+
+ #[inline]
+ fn with_part(
+ &mut self,
+ _part: Part,
+ mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
+ ) -> fmt::Result {
+ f(self)
+ }
+}
+
+/// A [`Writeable`] that writes out the given part.
+///
+/// # Examples
+///
+/// ```
+/// use writeable::adapters::WithPart;
+/// use writeable::assert_writeable_parts_eq;
+/// use writeable::Part;
+///
+/// // Simple usage:
+///
+/// const PART: Part = Part {
+/// category: "foo",
+/// value: "bar",
+/// };
+///
+/// assert_writeable_parts_eq!(
+/// WithPart {
+/// writeable: "Hello World",
+/// part: PART
+/// },
+/// "Hello World",
+/// [(0, 11, PART)],
+/// );
+///
+/// // Can be nested:
+///
+/// const PART2: Part = Part {
+/// category: "foo2",
+/// value: "bar2",
+/// };
+///
+/// assert_writeable_parts_eq!(
+/// WithPart {
+/// writeable: WithPart {
+/// writeable: "Hello World",
+/// part: PART
+/// },
+/// part: PART2
+/// },
+/// "Hello World",
+/// [(0, 11, PART), (0, 11, PART2)],
+/// );
+/// ```
+#[derive(Debug)]
+#[allow(clippy::exhaustive_structs)] // public adapter
+pub struct WithPart<T: ?Sized> {
+ pub part: Part,
+ pub writeable: T,
+}
+
+impl<T: Writeable + ?Sized> Writeable for WithPart<T> {
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ self.writeable.write_to(sink)
+ }
+
+ #[inline]
+ fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ sink.with_part(self.part, |w| self.writeable.write_to_parts(w))
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ self.writeable.writeable_length_hint()
+ }
+
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ self.writeable.write_to_string()
+ }
+}
+
+impl<T: Writeable + ?Sized> fmt::Display for WithPart<T> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Writeable::write_to(&self, f)
+ }
+}
diff --git a/vendor/writeable/src/testing.rs b/vendor/writeable/src/testing.rs
new file mode 100644
index 00000000..078ea9e2
--- /dev/null
+++ b/vendor/writeable/src/testing.rs
@@ -0,0 +1,78 @@
+// 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::*;
+use alloc::string::String;
+use alloc::vec::Vec;
+
+pub(crate) struct TestWriter {
+ pub(crate) string: String,
+ pub(crate) parts: Vec<(usize, usize, Part)>,
+}
+
+impl TestWriter {
+ pub(crate) fn finish(mut self) -> (String, Vec<(usize, usize, Part)>) {
+ // Sort by first open and last closed
+ self.parts
+ .sort_unstable_by_key(|(begin, end, _)| (*begin, end.wrapping_neg()));
+ (self.string, self.parts)
+ }
+}
+
+impl fmt::Write for TestWriter {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.string.write_str(s)
+ }
+ fn write_char(&mut self, c: char) -> fmt::Result {
+ self.string.write_char(c)
+ }
+}
+
+impl PartsWrite for TestWriter {
+ type SubPartsWrite = Self;
+ fn with_part(
+ &mut self,
+ part: Part,
+ mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
+ ) -> fmt::Result {
+ let start = self.string.len();
+ f(self)?;
+ let end = self.string.len();
+ if start < end {
+ self.parts.push((start, end, part));
+ }
+ Ok(())
+ }
+}
+
+#[allow(clippy::type_complexity)]
+pub fn writeable_to_parts_for_test<W: Writeable>(
+ writeable: &W,
+) -> (String, Vec<(usize, usize, Part)>) {
+ let mut writer = TestWriter {
+ string: alloc::string::String::new(),
+ parts: Vec::new(),
+ };
+ #[allow(clippy::expect_used)] // for test code
+ writeable
+ .write_to_parts(&mut writer)
+ .expect("String writer infallible");
+ writer.finish()
+}
+
+#[allow(clippy::type_complexity)]
+pub fn try_writeable_to_parts_for_test<W: TryWriteable>(
+ writeable: &W,
+) -> (String, Vec<(usize, usize, Part)>, Option<W::Error>) {
+ let mut writer = TestWriter {
+ string: alloc::string::String::new(),
+ parts: Vec::new(),
+ };
+ #[allow(clippy::expect_used)] // for test code
+ let result = writeable
+ .try_write_to_parts(&mut writer)
+ .expect("String writer infallible");
+ let (actual_str, actual_parts) = writer.finish();
+ (actual_str, actual_parts, result.err())
+}
diff --git a/vendor/writeable/src/to_string_or_borrow.rs b/vendor/writeable/src/to_string_or_borrow.rs
new file mode 100644
index 00000000..c1eb4236
--- /dev/null
+++ b/vendor/writeable/src/to_string_or_borrow.rs
@@ -0,0 +1,210 @@
+// 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 alloc::borrow::Cow;
+use alloc::string::String;
+use core::fmt;
+
+/// Bytes that have been partially validated as UTF-8 up to an offset.
+struct PartiallyValidatedUtf8<'a> {
+ // Safety Invariants:
+ // 1. The offset is less than or equal to the length of the slice.
+ // 2. The slice is valid UTF-8 up to the offset.
+ slice: &'a [u8],
+ offset: usize,
+}
+
+impl<'a> PartiallyValidatedUtf8<'a> {
+ fn new(slice: &'a [u8]) -> Self {
+ // Safety: Field invariants maintained here trivially:
+ // 1. The offset 0 is ≤ all possible lengths of slice
+ // 2. The slice contains nothing up to the offset zero
+ Self { slice, offset: 0 }
+ }
+
+ /// Check whether the given string is the next chunk of unvalidated bytes.
+ /// If so, increment offset and return true. Otherwise, return false.
+ fn try_push(&mut self, valid_str: &str) -> bool {
+ let new_offset = self.offset + valid_str.len();
+ if self.slice.get(self.offset..new_offset) == Some(valid_str.as_bytes()) {
+ // Safety: Field invariants maintained here:
+ // 1. In the line above, `self.slice.get()` returned `Some()` for `new_offset` at
+ // the end of a `Range`, so `new_offset` is ≤ the length of `self.slice`.
+ // 2. By invariant, we have already validated the string up to `self.offset`, and
+ // the portion of the slice between `self.offset` and `new_offset` is equal to
+ // `valid_str`, which is a `&str`, so the string is valid up to `new_offset`.
+ self.offset = new_offset;
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Return the validated portion as `&str`.
+ fn validated_as_str(&self) -> &'a str {
+ debug_assert!(self.offset <= self.slice.len());
+ // Safety: self.offset is a valid end index in a range (from field invariant)
+ let valid_slice = unsafe { self.slice.get_unchecked(..self.offset) };
+ debug_assert!(core::str::from_utf8(valid_slice).is_ok());
+ // Safety: the UTF-8 of slice has been validated up to offset (from field invariant)
+ unsafe { core::str::from_utf8_unchecked(valid_slice) }
+ }
+}
+
+enum SliceOrString<'a> {
+ Slice(PartiallyValidatedUtf8<'a>),
+ String(String),
+}
+
+/// This is an infallible impl. Functions always return Ok, not Err.
+impl fmt::Write for SliceOrString<'_> {
+ #[inline]
+ fn write_str(&mut self, other: &str) -> fmt::Result {
+ match self {
+ SliceOrString::Slice(slice) => {
+ if !slice.try_push(other) {
+ // We failed to match. Convert to owned.
+ let valid_str = slice.validated_as_str();
+ let mut owned = String::with_capacity(valid_str.len() + other.len());
+ owned.push_str(valid_str);
+ owned.push_str(other);
+ *self = SliceOrString::String(owned);
+ }
+ Ok(())
+ }
+ SliceOrString::String(owned) => owned.write_str(other),
+ }
+ }
+}
+
+impl<'a> SliceOrString<'a> {
+ #[inline]
+ fn new(slice: &'a [u8]) -> Self {
+ Self::Slice(PartiallyValidatedUtf8::new(slice))
+ }
+
+ #[inline]
+ fn finish(self) -> Cow<'a, str> {
+ match self {
+ SliceOrString::Slice(slice) => Cow::Borrowed(slice.validated_as_str()),
+ SliceOrString::String(owned) => Cow::Owned(owned),
+ }
+ }
+}
+
+/// Writes the contents of a `Writeable` to a string, returning a reference
+/// to a slice if it matches the provided reference bytes, and allocating a
+/// String otherwise.
+///
+/// This function is useful if you have borrowed bytes which you expect
+/// to be equal to a writeable a high percentage of the time.
+///
+/// You can also use this function to make a more efficient implementation of
+/// [`Writeable::write_to_string`].
+///
+/// # Examples
+///
+/// Basic usage and behavior:
+///
+/// ```
+/// use std::fmt;
+/// use std::borrow::Cow;
+/// 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" };
+///
+/// assert!(matches!(
+/// writeable::to_string_or_borrow(&message, b""),
+/// Cow::Owned(s) if s == "Hello, Alice!"
+/// ));
+/// assert!(matches!(
+/// writeable::to_string_or_borrow(&message, b"Hello"),
+/// Cow::Owned(s) if s == "Hello, Alice!"
+/// ));
+/// assert!(matches!(
+/// writeable::to_string_or_borrow(&message, b"Hello, Bob!"),
+/// Cow::Owned(s) if s == "Hello, Alice!"
+/// ));
+/// assert!(matches!(
+/// writeable::to_string_or_borrow(&message, b"Hello, Alice!"),
+/// Cow::Borrowed("Hello, Alice!")
+/// ));
+///
+/// // Borrowing can use a prefix:
+/// assert!(matches!(
+/// writeable::to_string_or_borrow(&message, b"Hello, Alice!..\xFF\x00\xFF"),
+/// Cow::Borrowed("Hello, Alice!")
+/// ));
+/// ```
+///
+/// Example use case: a function that transforms a string to lowercase.
+/// We are also able to write a more efficient implementation of
+/// [`Writeable::write_to_string`] in this situation.
+///
+/// ```
+/// use std::fmt;
+/// use std::borrow::Cow;
+/// use writeable::Writeable;
+///
+/// struct MakeAsciiLower<'a>(&'a str);
+///
+/// impl<'a> Writeable for MakeAsciiLower<'a> {
+/// fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+/// for c in self.0.chars() {
+/// sink.write_char(c.to_ascii_lowercase())?;
+/// }
+/// Ok(())
+/// }
+/// #[inline]
+/// fn write_to_string(&self) -> Cow<str> {
+/// writeable::to_string_or_borrow(self, self.0.as_bytes())
+/// }
+/// }
+///
+/// fn make_lowercase(input: &str) -> Cow<str> {
+/// let writeable = MakeAsciiLower(input);
+/// writeable::to_string_or_borrow(&writeable, input.as_bytes())
+/// }
+///
+/// assert!(matches!(
+/// make_lowercase("this is lowercase"),
+/// Cow::Borrowed("this is lowercase")
+/// ));
+/// assert!(matches!(
+/// make_lowercase("this is UPPERCASE"),
+/// Cow::Owned(s) if s == "this is uppercase"
+/// ));
+///
+/// assert!(matches!(
+/// MakeAsciiLower("this is lowercase").write_to_string(),
+/// Cow::Borrowed("this is lowercase")
+/// ));
+/// assert!(matches!(
+/// MakeAsciiLower("this is UPPERCASE").write_to_string(),
+/// Cow::Owned(s) if s == "this is uppercase"
+/// ));
+/// ```
+pub fn to_string_or_borrow<'a>(
+ writeable: &impl Writeable,
+ reference_bytes: &'a [u8],
+) -> Cow<'a, str> {
+ let mut sink = SliceOrString::new(reference_bytes);
+ let _ = writeable.write_to(&mut sink);
+ sink.finish()
+}
diff --git a/vendor/writeable/src/try_writeable.rs b/vendor/writeable/src/try_writeable.rs
new file mode 100644
index 00000000..aa918bbd
--- /dev/null
+++ b/vendor/writeable/src/try_writeable.rs
@@ -0,0 +1,439 @@
+// 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 super::*;
+use crate::parts_write_adapter::CoreWriteAsPartsWrite;
+use core::convert::Infallible;
+
+/// A writeable object that can fail while writing.
+///
+/// The default [`Writeable`] trait returns a [`fmt::Error`], which originates from the sink.
+/// In contrast, this trait allows the _writeable itself_ to trigger an error as well.
+///
+/// Implementations are expected to always make a _best attempt_ at writing to the sink
+/// and should write replacement values in the error state. Therefore, the returned `Result`
+/// can be safely ignored to emulate a "lossy" mode.
+///
+/// Any error substrings should be annotated with [`Part::ERROR`].
+///
+/// # Implementer Notes
+///
+/// This trait requires that implementers make a _best attempt_ at writing to the sink,
+/// _even in the error state_, such as with a placeholder or fallback string.
+///
+/// In [`TryWriteable::try_write_to_parts()`], error substrings should be annotated with
+/// [`Part::ERROR`]. Because of this, writing to parts is not default-implemented like
+/// it is on [`Writeable`].
+///
+/// The trait is implemented on [`Result<T, E>`] where `T` and `E` both implement [`Writeable`];
+/// In the `Ok` case, `T` is written, and in the `Err` case, `E` is written as a fallback value.
+/// This impl, which writes [`Part::ERROR`], can be used as a basis for more advanced impls.
+///
+/// # Examples
+///
+/// Implementing on a custom type:
+///
+/// ```
+/// use core::fmt;
+/// use writeable::LengthHint;
+/// use writeable::PartsWrite;
+/// use writeable::TryWriteable;
+///
+/// #[derive(Debug, PartialEq, Eq)]
+/// enum HelloWorldWriteableError {
+/// MissingName,
+/// }
+///
+/// #[derive(Debug, PartialEq, Eq)]
+/// struct HelloWorldWriteable {
+/// pub name: Option<&'static str>,
+/// }
+///
+/// impl TryWriteable for HelloWorldWriteable {
+/// type Error = HelloWorldWriteableError;
+///
+/// fn try_write_to_parts<S: PartsWrite + ?Sized>(
+/// &self,
+/// sink: &mut S,
+/// ) -> Result<Result<(), Self::Error>, fmt::Error> {
+/// sink.write_str("Hello, ")?;
+/// // Use `impl TryWriteable for Result` to generate the error part:
+/// let err = self.name.ok_or("nobody").try_write_to_parts(sink)?.err();
+/// sink.write_char('!')?;
+/// // Return a doubly-wrapped Result.
+/// // The outer Result is for fmt::Error, handled by the `?`s above.
+/// // The inner Result is for our own Self::Error.
+/// if err.is_none() {
+/// Ok(Ok(()))
+/// } else {
+/// Ok(Err(HelloWorldWriteableError::MissingName))
+/// }
+/// }
+///
+/// fn writeable_length_hint(&self) -> LengthHint {
+/// self.name.ok_or("nobody").writeable_length_hint() + 8
+/// }
+/// }
+///
+/// // Success case:
+/// writeable::assert_try_writeable_eq!(
+/// HelloWorldWriteable {
+/// name: Some("Alice")
+/// },
+/// "Hello, Alice!"
+/// );
+///
+/// // Failure case, including the ERROR part:
+/// writeable::assert_try_writeable_parts_eq!(
+/// HelloWorldWriteable { name: None },
+/// "Hello, nobody!",
+/// Err(HelloWorldWriteableError::MissingName),
+/// [(7, 13, writeable::Part::ERROR)]
+/// );
+/// ```
+pub trait TryWriteable {
+ type Error;
+
+ /// Writes the content of this writeable to a sink.
+ ///
+ /// If the sink hits an error, writing immediately ends,
+ /// `Err(`[`fmt::Error`]`)` is returned, and the sink does not contain valid output.
+ ///
+ /// If the writeable hits an error, writing is continued with a replacement value,
+ /// `Ok(Err(`[`TryWriteable::Error`]`))` is returned, and the caller may continue using the sink.
+ ///
+ /// # Lossy Mode
+ ///
+ /// The [`fmt::Error`] should always be handled, but the [`TryWriteable::Error`] can be
+ /// ignored if a fallback string is desired instead of an error.
+ ///
+ /// To handle the sink error, but not the writeable error, write:
+ ///
+ /// ```
+ /// # use writeable::TryWriteable;
+ /// # let my_writeable: Result<&str, &str> = Ok("");
+ /// # let mut sink = String::new();
+ /// let _ = my_writeable.try_write_to(&mut sink)?;
+ /// # Ok::<(), core::fmt::Error>(())
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// The following examples use `Result<&str, usize>`, which implements [`TryWriteable`] because both `&str` and `usize` do.
+ ///
+ /// Success case:
+ ///
+ /// ```
+ /// use writeable::TryWriteable;
+ ///
+ /// let w: Result<&str, usize> = Ok("success");
+ /// let mut sink = String::new();
+ /// let result = w.try_write_to(&mut sink);
+ ///
+ /// assert_eq!(result, Ok(Ok(())));
+ /// assert_eq!(sink, "success");
+ /// ```
+ ///
+ /// Failure case:
+ ///
+ /// ```
+ /// use writeable::TryWriteable;
+ ///
+ /// let w: Result<&str, usize> = Err(44);
+ /// let mut sink = String::new();
+ /// let result = w.try_write_to(&mut sink);
+ ///
+ /// assert_eq!(result, Ok(Err(44)));
+ /// assert_eq!(sink, "44");
+ /// ```
+ fn try_write_to<W: fmt::Write + ?Sized>(
+ &self,
+ sink: &mut W,
+ ) -> Result<Result<(), Self::Error>, fmt::Error> {
+ self.try_write_to_parts(&mut CoreWriteAsPartsWrite(sink))
+ }
+
+ /// Writes the content of this writeable to a sink with parts (annotations).
+ ///
+ /// For more information, see:
+ ///
+ /// - [`TryWriteable::try_write_to()`] for the general behavior.
+ /// - [`TryWriteable`] for an example with parts.
+ /// - [`Part`] for more about parts.
+ fn try_write_to_parts<S: PartsWrite + ?Sized>(
+ &self,
+ sink: &mut S,
+ ) -> Result<Result<(), Self::Error>, fmt::Error>;
+
+ /// Returns a hint for the number of UTF-8 bytes that will be written to the sink.
+ ///
+ /// This function returns the length of the "lossy mode" string; for more information,
+ /// see [`TryWriteable::try_write_to()`].
+ fn writeable_length_hint(&self) -> LengthHint {
+ LengthHint::undefined()
+ }
+
+ /// Writes the content of this writeable to a string.
+ ///
+ /// In the failure case, this function returns the error and the best-effort string ("lossy mode").
+ ///
+ /// Examples
+ ///
+ /// ```
+ /// # use std::borrow::Cow;
+ /// # use writeable::TryWriteable;
+ /// // use the best-effort string
+ /// let r1: Cow<str> = Ok::<&str, u8>("ok")
+ /// .try_write_to_string()
+ /// .unwrap_or_else(|(_, s)| s);
+ /// // propagate the error
+ /// let r2: Result<Cow<str>, u8> = Ok::<&str, u8>("ok")
+ /// .try_write_to_string()
+ /// .map_err(|(e, _)| e);
+ /// ```
+ fn try_write_to_string(&self) -> Result<Cow<str>, (Self::Error, Cow<str>)> {
+ let hint = self.writeable_length_hint();
+ if hint.is_zero() {
+ return Ok(Cow::Borrowed(""));
+ }
+ let mut output = String::with_capacity(hint.capacity());
+ match self
+ .try_write_to(&mut output)
+ .unwrap_or_else(|fmt::Error| Ok(()))
+ {
+ Ok(()) => Ok(Cow::Owned(output)),
+ Err(e) => Err((e, Cow::Owned(output))),
+ }
+ }
+}
+
+impl<T, E> TryWriteable for Result<T, E>
+where
+ T: Writeable,
+ E: Writeable + Clone,
+{
+ type Error = E;
+
+ #[inline]
+ fn try_write_to<W: fmt::Write + ?Sized>(
+ &self,
+ sink: &mut W,
+ ) -> Result<Result<(), Self::Error>, fmt::Error> {
+ match self {
+ Ok(t) => t.write_to(sink).map(Ok),
+ Err(e) => e.write_to(sink).map(|()| Err(e.clone())),
+ }
+ }
+
+ #[inline]
+ fn try_write_to_parts<S: PartsWrite + ?Sized>(
+ &self,
+ sink: &mut S,
+ ) -> Result<Result<(), Self::Error>, fmt::Error> {
+ match self {
+ Ok(t) => t.write_to_parts(sink).map(Ok),
+ Err(e) => sink
+ .with_part(Part::ERROR, |sink| e.write_to_parts(sink))
+ .map(|()| Err(e.clone())),
+ }
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ match self {
+ Ok(t) => t.writeable_length_hint(),
+ Err(e) => e.writeable_length_hint(),
+ }
+ }
+
+ #[inline]
+ fn try_write_to_string(&self) -> Result<Cow<str>, (Self::Error, Cow<str>)> {
+ match self {
+ Ok(t) => Ok(t.write_to_string()),
+ Err(e) => Err((e.clone(), e.write_to_string())),
+ }
+ }
+}
+
+/// A wrapper around [`TryWriteable`] that implements [`Writeable`]
+/// if [`TryWriteable::Error`] is [`Infallible`].
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+#[allow(clippy::exhaustive_structs)] // transparent newtype
+pub struct TryWriteableInfallibleAsWriteable<T>(pub T);
+
+impl<T> Writeable for TryWriteableInfallibleAsWriteable<T>
+where
+ T: TryWriteable<Error = Infallible>,
+{
+ #[inline]
+ fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
+ match self.0.try_write_to(sink) {
+ Ok(Ok(())) => Ok(()),
+ Ok(Err(infallible)) => match infallible {},
+ Err(e) => Err(e),
+ }
+ }
+
+ #[inline]
+ fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
+ match self.0.try_write_to_parts(sink) {
+ Ok(Ok(())) => Ok(()),
+ Ok(Err(infallible)) => match infallible {},
+ Err(e) => Err(e),
+ }
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ self.0.writeable_length_hint()
+ }
+
+ #[inline]
+ fn write_to_string(&self) -> Cow<str> {
+ match self.0.try_write_to_string() {
+ Ok(s) => s,
+ Err((infallible, _)) => match infallible {},
+ }
+ }
+}
+
+impl<T> fmt::Display for TryWriteableInfallibleAsWriteable<T>
+where
+ T: TryWriteable<Error = Infallible>,
+{
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.write_to(f)
+ }
+}
+
+/// A wrapper around [`Writeable`] that implements [`TryWriteable`]
+/// with [`TryWriteable::Error`] set to [`Infallible`].
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+#[allow(clippy::exhaustive_structs)] // transparent newtype
+pub struct WriteableAsTryWriteableInfallible<T>(pub T);
+
+impl<T> TryWriteable for WriteableAsTryWriteableInfallible<T>
+where
+ T: Writeable,
+{
+ type Error = Infallible;
+
+ #[inline]
+ fn try_write_to<W: fmt::Write + ?Sized>(
+ &self,
+ sink: &mut W,
+ ) -> Result<Result<(), Infallible>, fmt::Error> {
+ self.0.write_to(sink).map(Ok)
+ }
+
+ #[inline]
+ fn try_write_to_parts<S: PartsWrite + ?Sized>(
+ &self,
+ sink: &mut S,
+ ) -> Result<Result<(), Infallible>, fmt::Error> {
+ self.0.write_to_parts(sink).map(Ok)
+ }
+
+ #[inline]
+ fn writeable_length_hint(&self) -> LengthHint {
+ self.0.writeable_length_hint()
+ }
+
+ #[inline]
+ fn try_write_to_string(&self) -> Result<Cow<str>, (Infallible, Cow<str>)> {
+ Ok(self.0.write_to_string())
+ }
+}
+
+/// Testing macros for types implementing [`TryWriteable`].
+///
+/// Arguments, in order:
+///
+/// 1. The [`TryWriteable`] under test
+/// 2. The expected string value
+/// 3. The expected result value, or `Ok(())` if omitted
+/// 3. [`*_parts_eq`] only: a list of parts (`[(start, end, Part)]`)
+///
+/// Any remaining arguments get passed to `format!`
+///
+/// The macros tests the following:
+///
+/// - Equality of string content
+/// - Equality of parts ([`*_parts_eq`] only)
+/// - Validity of size hint
+///
+/// For a usage example, see [`TryWriteable`].
+///
+/// [`*_parts_eq`]: assert_try_writeable_parts_eq
+#[macro_export]
+macro_rules! assert_try_writeable_eq {
+ ($actual_writeable:expr, $expected_str:expr $(,)?) => {
+ $crate::assert_try_writeable_eq!($actual_writeable, $expected_str, Ok(()))
+ };
+ ($actual_writeable:expr, $expected_str:expr, $expected_result:expr $(,)?) => {
+ $crate::assert_try_writeable_eq!($actual_writeable, $expected_str, $expected_result, "")
+ };
+ ($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
+ $crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
+ }};
+ (@internal, $actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
+ use $crate::TryWriteable;
+ let actual_writeable = &$actual_writeable;
+ let (actual_str, actual_parts, actual_error) = $crate::_internal::try_writeable_to_parts_for_test(actual_writeable);
+ assert_eq!(actual_str, $expected_str, $($arg)*);
+ assert_eq!(actual_error, Result::<(), _>::from($expected_result).err(), $($arg)*);
+ let actual_result = match actual_writeable.try_write_to_string() {
+ Ok(actual_cow_str) => {
+ assert_eq!(actual_cow_str, $expected_str, $($arg)+);
+ Ok(())
+ }
+ Err((e, actual_cow_str)) => {
+ assert_eq!(actual_cow_str, $expected_str, $($arg)+);
+ Err(e)
+ }
+ };
+ assert_eq!(actual_result, Result::<(), _>::from($expected_result), $($arg)*);
+ let length_hint = actual_writeable.writeable_length_hint();
+ assert!(
+ length_hint.0 <= actual_str.len(),
+ "hint lower bound {} larger than actual length {}: {}",
+ length_hint.0, actual_str.len(), format!($($arg)*),
+ );
+ if let Some(upper) = length_hint.1 {
+ assert!(
+ actual_str.len() <= upper,
+ "hint upper bound {} smaller than actual length {}: {}",
+ length_hint.0, actual_str.len(), format!($($arg)*),
+ );
+ }
+ actual_parts // return for assert_try_writeable_parts_eq
+ }};
+}
+
+/// See [`assert_try_writeable_eq`].
+#[macro_export]
+macro_rules! assert_try_writeable_parts_eq {
+ ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => {
+ $crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, Ok(()), $expected_parts)
+ };
+ ($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr $(,)?) => {
+ $crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, $expected_result, $expected_parts, "")
+ };
+ ($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr, $($arg:tt)+) => {{
+ let actual_parts = $crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
+ assert_eq!(actual_parts, $expected_parts, $($arg)+);
+ }};
+}
+
+#[test]
+fn test_result_try_writeable() {
+ let mut result: Result<&str, usize> = Ok("success");
+ assert_try_writeable_eq!(result, "success");
+ result = Err(44);
+ assert_try_writeable_eq!(result, "44", Err(44));
+ assert_try_writeable_parts_eq!(result, "44", Err(44), [(0, 2, Part::ERROR)])
+}