/rust/registry/src/index.crates.io-1949cf8c6b5b557f/writeable-0.6.3/src/lib.rs
Line | Count | Source |
1 | | // This file is part of ICU4X. For terms of use, please see the file |
2 | | // called LICENSE at the top level of the ICU4X source tree |
3 | | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | | |
5 | | // https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations |
6 | | #![cfg_attr(not(any(test, doc)), no_std)] |
7 | | #![cfg_attr( |
8 | | not(test), |
9 | | deny( |
10 | | clippy::indexing_slicing, |
11 | | clippy::unwrap_used, |
12 | | clippy::expect_used, |
13 | | clippy::panic, |
14 | | ) |
15 | | )] |
16 | | #![warn(missing_docs)] |
17 | | |
18 | | //! This crate defines [`Writeable`], a trait representing an object that can be written to a |
19 | | //! sink implementing `std::fmt::Write`. It is an alternative to `std::fmt::Display` with the |
20 | | //! addition of a function indicating the number of bytes to be written. |
21 | | //! |
22 | | //! `Writeable` improves upon `std::fmt::Display` in two ways: |
23 | | //! |
24 | | //! 1. More efficient, since the sink can pre-allocate bytes. |
25 | | //! 2. Smaller code, since the format machinery can be short-circuited. |
26 | | //! |
27 | | //! This crate also exports [`TryWriteable`], a writeable that supports a custom error. |
28 | | //! |
29 | | //! # Benchmarks |
30 | | //! |
31 | | //! The benchmarks to generate the following data can be found in the `benches` directory. |
32 | | //! |
33 | | //! | Case | `Writeable` | `Display` | |
34 | | //! |---|---|---| |
35 | | //! | Create string from single-string message (139 chars) | 15.642 ns | 19.251 ns | |
36 | | //! | Create string from complex message | 35.830 ns | 89.478 ns | |
37 | | //! | Write complex message to buffer | 57.336 ns | 64.408 ns | |
38 | | //! |
39 | | //! # Examples |
40 | | //! |
41 | | //! ``` |
42 | | //! use std::fmt; |
43 | | //! use writeable::assert_writeable_eq; |
44 | | //! use writeable::LengthHint; |
45 | | //! use writeable::Writeable; |
46 | | //! |
47 | | //! struct WelcomeMessage<'s> { |
48 | | //! pub name: &'s str, |
49 | | //! } |
50 | | //! |
51 | | //! impl<'s> Writeable for WelcomeMessage<'s> { |
52 | | //! fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
53 | | //! sink.write_str("Hello, ")?; |
54 | | //! sink.write_str(self.name)?; |
55 | | //! sink.write_char('!')?; |
56 | | //! Ok(()) |
57 | | //! } |
58 | | //! |
59 | | //! fn writeable_length_hint(&self) -> LengthHint { |
60 | | //! // "Hello, " + '!' + length of name |
61 | | //! LengthHint::exact(8 + self.name.len()) |
62 | | //! } |
63 | | //! } |
64 | | //! |
65 | | //! let message = WelcomeMessage { name: "Alice" }; |
66 | | //! assert_writeable_eq!(&message, "Hello, Alice!"); |
67 | | //! |
68 | | //! // Types implementing `Writeable` are recommended to also implement `fmt::Display`. |
69 | | //! // This can be simply done by redirecting to the `Writeable` implementation: |
70 | | //! writeable::impl_display_with_writeable!(WelcomeMessage<'_>); |
71 | | //! assert_eq!(message.to_string(), "Hello, Alice!"); |
72 | | //! ``` |
73 | | //! |
74 | | //! [`ICU4X`]: ../icu/index.html |
75 | | |
76 | | #[cfg(feature = "alloc")] |
77 | | extern crate alloc; |
78 | | |
79 | | mod cmp; |
80 | | mod concat; |
81 | | #[cfg(feature = "either")] |
82 | | mod either; |
83 | | mod impls; |
84 | | mod ops; |
85 | | mod parts_write_adapter; |
86 | | #[cfg(feature = "alloc")] |
87 | | mod testing; |
88 | | #[cfg(feature = "alloc")] |
89 | | mod to_string_or_borrow; |
90 | | mod try_writeable; |
91 | | |
92 | | #[cfg(feature = "alloc")] |
93 | | use alloc::borrow::Cow; |
94 | | |
95 | | #[cfg(feature = "alloc")] |
96 | | use alloc::string::String; |
97 | | use core::fmt; |
98 | | |
99 | | pub use cmp::{cmp_str, cmp_utf8}; |
100 | | pub use concat::concat_writeable; |
101 | | #[cfg(feature = "alloc")] |
102 | | pub use to_string_or_borrow::to_string_or_borrow; |
103 | | pub use try_writeable::TryWriteable; |
104 | | |
105 | | /// Helper types for trait impls. |
106 | | pub mod adapters { |
107 | | use super::*; |
108 | | |
109 | | pub use concat::Concat; |
110 | | pub use parts_write_adapter::CoreWriteAsPartsWrite; |
111 | | pub use parts_write_adapter::WithPart; |
112 | | pub use try_writeable::TryWriteableInfallibleAsWriteable; |
113 | | pub use try_writeable::WriteableAsTryWriteableInfallible; |
114 | | |
115 | | /// A lossy wrapper for a [`TryWriteable`] that implements [`Writeable`] |
116 | | /// and ignores any errors. |
117 | | #[derive(Debug)] |
118 | | #[allow(clippy::exhaustive_structs)] // newtype |
119 | | pub struct LossyWrap<T>(pub T); |
120 | | |
121 | | impl<T: TryWriteable> Writeable for LossyWrap<T> { |
122 | 0 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
123 | 0 | let _ = self.0.try_write_to(sink)?; |
124 | 0 | Ok(()) |
125 | 0 | } |
126 | | |
127 | 0 | fn writeable_length_hint(&self) -> LengthHint { |
128 | 0 | self.0.writeable_length_hint() |
129 | 0 | } |
130 | | } |
131 | | |
132 | | impl<T: TryWriteable> fmt::Display for LossyWrap<T> { |
133 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
134 | 0 | let _ = self.0.try_write_to(f)?; |
135 | 0 | Ok(()) |
136 | 0 | } |
137 | | } |
138 | | } |
139 | | |
140 | | #[doc(hidden)] // for testing and macros |
141 | | pub mod _internal { |
142 | | #[cfg(feature = "alloc")] |
143 | | pub use super::testing::try_writeable_to_parts_for_test; |
144 | | #[cfg(feature = "alloc")] |
145 | | pub use super::testing::writeable_to_parts_for_test; |
146 | | #[cfg(feature = "alloc")] |
147 | | pub use alloc::string::String; |
148 | | } |
149 | | |
150 | | /// A hint to help consumers of `Writeable` pre-allocate bytes before they call |
151 | | /// [`write_to`](Writeable::write_to). |
152 | | /// |
153 | | /// This behaves like `Iterator::size_hint`: it is a tuple where the first element is the |
154 | | /// lower bound, and the second element is the upper bound. If the upper bound is `None` |
155 | | /// either there is no known upper bound, or the upper bound is larger than `usize`. |
156 | | /// |
157 | | /// `LengthHint` implements std`::ops::{Add, Mul}` and similar traits for easy composition. |
158 | | /// During computation, the lower bound will saturate at `usize::MAX`, while the upper |
159 | | /// bound will become `None` if `usize::MAX` is exceeded. |
160 | | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
161 | | #[non_exhaustive] |
162 | | pub struct LengthHint(pub usize, pub Option<usize>); |
163 | | |
164 | | impl LengthHint { |
165 | | /// Unknown |
166 | 0 | pub fn undefined() -> Self { |
167 | 0 | Self(0, None) |
168 | 0 | } |
169 | | |
170 | | /// `write_to` will use exactly n bytes. |
171 | 0 | pub fn exact(n: usize) -> Self { |
172 | 0 | Self(n, Some(n)) |
173 | 0 | } |
174 | | |
175 | | /// `write_to` will use at least n bytes. |
176 | 0 | pub fn at_least(n: usize) -> Self { |
177 | 0 | Self(n, None) |
178 | 0 | } |
179 | | |
180 | | /// `write_to` will use at most n bytes. |
181 | 0 | pub fn at_most(n: usize) -> Self { |
182 | 0 | Self(0, Some(n)) |
183 | 0 | } |
184 | | |
185 | | /// `write_to` will use between `n` and `m` bytes. |
186 | 0 | pub fn between(n: usize, m: usize) -> Self { |
187 | 0 | Self(Ord::min(n, m), Some(Ord::max(n, m))) |
188 | 0 | } |
189 | | |
190 | | /// Returns a recommendation for the number of bytes to pre-allocate. |
191 | | /// If an upper bound exists, this is used, otherwise the lower bound |
192 | | /// (which might be 0). |
193 | | /// |
194 | | /// # Examples |
195 | | /// |
196 | | /// ``` |
197 | | /// use writeable::Writeable; |
198 | | /// |
199 | | /// fn pre_allocate_string(w: &impl Writeable) -> String { |
200 | | /// String::with_capacity(w.writeable_length_hint().capacity()) |
201 | | /// } |
202 | | /// ``` |
203 | 0 | pub fn capacity(&self) -> usize { |
204 | 0 | self.1.unwrap_or(self.0) |
205 | 0 | } |
206 | | |
207 | | /// Returns whether the `LengthHint` indicates that the string is exactly 0 bytes long. |
208 | 0 | pub fn is_zero(&self) -> bool { |
209 | 0 | self.1 == Some(0) |
210 | 0 | } |
211 | | } |
212 | | |
213 | | /// [`Part`]s are used as annotations for formatted strings. |
214 | | /// |
215 | | /// For example, a string like `Alice, Bob` could assign a `NAME` part to the |
216 | | /// substrings `Alice` and `Bob`, and a `PUNCTUATION` part to `, `. This allows |
217 | | /// for example to apply styling only to names. |
218 | | /// |
219 | | /// `Part` contains two fields, whose usage is left up to the producer of the [`Writeable`]. |
220 | | /// Conventionally, the `category` field will identify the formatting logic that produces |
221 | | /// the string/parts, whereas the `value` field will have semantic meaning. `NAME` and |
222 | | /// `PUNCTUATION` could thus be defined as |
223 | | /// ``` |
224 | | /// # use writeable::Part; |
225 | | /// const NAME: Part = Part { |
226 | | /// category: "userlist", |
227 | | /// value: "name", |
228 | | /// }; |
229 | | /// const PUNCTUATION: Part = Part { |
230 | | /// category: "userlist", |
231 | | /// value: "punctuation", |
232 | | /// }; |
233 | | /// ``` |
234 | | /// |
235 | | /// That said, consumers should not usually have to inspect `Part` internals. Instead, |
236 | | /// formatters should expose the `Part`s they produces as constants. |
237 | | #[derive(Clone, Copy, Debug, PartialEq)] |
238 | | #[allow(clippy::exhaustive_structs)] // stable |
239 | | #[allow(missing_docs)] // behavior not defined, explained in type docs |
240 | | pub struct Part { |
241 | | pub category: &'static str, |
242 | | pub value: &'static str, |
243 | | } |
244 | | |
245 | | impl Part { |
246 | | /// A part that should annotate error segments in [`TryWriteable`] output. |
247 | | /// |
248 | | /// For an example, see [`TryWriteable`]. |
249 | | pub const ERROR: Part = Part { |
250 | | category: "writeable", |
251 | | value: "error", |
252 | | }; |
253 | | } |
254 | | |
255 | | /// A sink that supports annotating parts of the string with [`Part`]s. |
256 | | pub trait PartsWrite: fmt::Write { |
257 | | /// The recursive sink |
258 | | type SubPartsWrite: PartsWrite + ?Sized; |
259 | | |
260 | | /// Annotates all strings written by the closure with the given [`Part`]. |
261 | | fn with_part( |
262 | | &mut self, |
263 | | part: Part, |
264 | | f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result, |
265 | | ) -> fmt::Result; |
266 | | } |
267 | | |
268 | | /// `Writeable` is an alternative to `std::fmt::Display` with the addition of a length function. |
269 | | pub trait Writeable { |
270 | | /// Writes a string to the given sink. Errors from the sink are bubbled up. |
271 | | /// The default implementation delegates to `write_to_parts`, and discards any |
272 | | /// `Part` annotations. |
273 | 0 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
274 | 0 | self.write_to_parts(&mut parts_write_adapter::CoreWriteAsPartsWrite(sink)) |
275 | 0 | } |
276 | | |
277 | | /// Write bytes and `Part` annotations to the given sink. Errors from the |
278 | | /// sink are bubbled up. The default implementation delegates to `write_to`, |
279 | | /// and doesn't produce any `Part` annotations. |
280 | 0 | fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result { |
281 | 0 | self.write_to(sink) |
282 | 0 | } |
283 | | |
284 | | /// Returns a hint for the number of UTF-8 bytes that will be written to the sink. |
285 | | /// |
286 | | /// Override this method if it can be computed quickly. |
287 | 0 | fn writeable_length_hint(&self) -> LengthHint { |
288 | 0 | LengthHint::undefined() |
289 | 0 | } |
290 | | |
291 | | /// Returns a `&str` that matches the output of `write_to`, if possible. |
292 | | /// |
293 | | /// This method is used to avoid materializing a [`String`] in `write_to_string`. |
294 | 0 | fn writeable_borrow(&self) -> Option<&str> { |
295 | 0 | None |
296 | 0 | } |
297 | | |
298 | | /// Creates a new string with the data from this `Writeable`. |
299 | | /// |
300 | | /// Unlike [`to_string`](ToString::to_string), this does not pull in `core::fmt` |
301 | | /// code, and borrows the string if possible. |
302 | | /// |
303 | | /// To remove the `Cow` wrapper, call `.into_owned()` or `.as_str()` as appropriate. |
304 | | /// |
305 | | /// # Examples |
306 | | /// |
307 | | /// Inspect a [`Writeable`] before writing it to the sink: |
308 | | /// |
309 | | /// ``` |
310 | | /// use core::fmt::{Result, Write}; |
311 | | /// use writeable::Writeable; |
312 | | /// |
313 | | /// fn write_if_ascii<W, S>(w: &W, sink: &mut S) -> Result |
314 | | /// where |
315 | | /// W: Writeable + ?Sized, |
316 | | /// S: Write + ?Sized, |
317 | | /// { |
318 | | /// let s = w.write_to_string(); |
319 | | /// if s.is_ascii() { |
320 | | /// sink.write_str(&s) |
321 | | /// } else { |
322 | | /// Ok(()) |
323 | | /// } |
324 | | /// } |
325 | | /// ``` |
326 | | /// |
327 | | /// Convert the `Writeable` into a fully owned `String`: |
328 | | /// |
329 | | /// ``` |
330 | | /// use writeable::Writeable; |
331 | | /// |
332 | | /// fn make_string(w: &impl Writeable) -> String { |
333 | | /// w.write_to_string().into_owned() |
334 | | /// } |
335 | | /// ``` |
336 | | /// |
337 | | /// # Note to implementors |
338 | | /// |
339 | | /// This method has a default implementation in terms of `writeable_borrow`, |
340 | | /// `writeable_length_hint`, and `write_to`. The only case |
341 | | /// where this should be implemented is if the computation of `writeable_borrow` |
342 | | /// requires a full invocation of `write_to`. In this case, implement this |
343 | | /// using [`to_string_or_borrow`]. |
344 | | /// |
345 | | /// # `alloc` Cargo feature |
346 | | /// |
347 | | /// Calling or implementing this method requires the `alloc` Cargo feature. |
348 | | /// However, as all the methods required by the default implementation do |
349 | | /// not require the `alloc` Cargo feature, a caller that uses the feature |
350 | | /// can still call this on types from crates that don't use the `alloc` |
351 | | /// Cargo feature. |
352 | | #[cfg(feature = "alloc")] |
353 | | fn write_to_string(&self) -> Cow<'_, str> { |
354 | | if let Some(borrow) = self.writeable_borrow() { |
355 | | return Cow::Borrowed(borrow); |
356 | | } |
357 | | let hint = self.writeable_length_hint(); |
358 | | if hint.is_zero() { |
359 | | return Cow::Borrowed(""); |
360 | | } |
361 | | let mut output = String::with_capacity(hint.capacity()); |
362 | | let _ = self.write_to(&mut output); |
363 | | Cow::Owned(output) |
364 | | } |
365 | | } |
366 | | |
367 | | /// Implements [`Display`](core::fmt::Display) for types that implement [`Writeable`]. |
368 | | /// |
369 | | /// It's recommended to do this for every [`Writeable`] type, as it will add |
370 | | /// support for `core::fmt` features like [`fmt!`](std::fmt), |
371 | | /// [`print!`](std::print), [`write!`](std::write), etc. |
372 | | /// |
373 | | /// This macro also adds a concrete `to_string` function. This function will shadow the |
374 | | /// standard library `ToString`, using the more efficient writeable-based code path. |
375 | | /// To add only `Display`, use the `@display` macro variant. |
376 | | #[macro_export] |
377 | | macro_rules! impl_display_with_writeable { |
378 | | (@display, $type:ty) => { |
379 | | /// This trait is implemented for compatibility with [`fmt!`](core::fmt). |
380 | | /// To create a string, [`Writeable::write_to_string`] is usually more efficient. |
381 | | impl core::fmt::Display for $type { |
382 | | #[inline] |
383 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
384 | 0 | $crate::Writeable::write_to(&self, f) |
385 | 0 | } Unexecuted instantiation: <icu_locale_core::data::DataLocale as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::langid::LanguageIdentifier as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::data::DataLocale as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::Unicode as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::transform::Transform as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::other::Other as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::transform::value::Value as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::transform::fields::Fields as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::private::Private as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::variants::Variants as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::attributes::Attributes as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::keywords::Keywords as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::value::Value as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::locale::Locale as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::variant::Variant as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::private::other::Subtag as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::key::Key as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::attribute::Attribute as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::transform::key::Key as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::region::Region as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::script::Script as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::language::Language as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::Extensions as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::subtags::Subtag as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::subdivision::SubdivisionSuffix as core::fmt::Display>::fmt Unexecuted instantiation: <icu_locale_core::extensions::unicode::subdivision::SubdivisionId as core::fmt::Display>::fmt |
386 | | } |
387 | | }; |
388 | | ($type:ty $(, #[$alloc_feature:meta])? ) => { |
389 | | $crate::impl_display_with_writeable!(@display, $type); |
390 | | $(#[$alloc_feature])? |
391 | | impl $type { |
392 | | /// Converts the given value to a `String`. |
393 | | /// |
394 | | /// Under the hood, this uses an efficient [`Writeable`] implementation. |
395 | | /// However, in order to avoid allocating a string, it is more efficient |
396 | | /// to use [`Writeable`] directly. |
397 | | pub fn to_string(&self) -> $crate::_internal::String { |
398 | | $crate::Writeable::write_to_string(self).into_owned() |
399 | | } |
400 | | } |
401 | | }; |
402 | | } |
403 | | |
404 | | /// Testing macros for types implementing [`Writeable`]. |
405 | | /// |
406 | | /// Arguments, in order: |
407 | | /// |
408 | | /// 1. The [`Writeable`] under test |
409 | | /// 2. The expected string value |
410 | | /// 3. [`*_parts_eq`] only: a list of parts (`[(start, end, Part)]`) |
411 | | /// |
412 | | /// Any remaining arguments get passed to `format!` |
413 | | /// |
414 | | /// The macros tests the following: |
415 | | /// |
416 | | /// - Equality of string content |
417 | | /// - Equality of parts ([`*_parts_eq`] only) |
418 | | /// - Validity of size hint |
419 | | /// |
420 | | /// # Examples |
421 | | /// |
422 | | /// ``` |
423 | | /// # use writeable::Writeable; |
424 | | /// # use writeable::LengthHint; |
425 | | /// # use writeable::Part; |
426 | | /// # use writeable::assert_writeable_eq; |
427 | | /// # use writeable::assert_writeable_parts_eq; |
428 | | /// # use std::fmt::{self, Write}; |
429 | | /// |
430 | | /// const WORD: Part = Part { |
431 | | /// category: "foo", |
432 | | /// value: "word", |
433 | | /// }; |
434 | | /// |
435 | | /// struct Demo; |
436 | | /// impl Writeable for Demo { |
437 | | /// fn write_to_parts<S: writeable::PartsWrite + ?Sized>( |
438 | | /// &self, |
439 | | /// sink: &mut S, |
440 | | /// ) -> fmt::Result { |
441 | | /// sink.with_part(WORD, |w| w.write_str("foo")) |
442 | | /// } |
443 | | /// fn writeable_length_hint(&self) -> LengthHint { |
444 | | /// LengthHint::exact(3) |
445 | | /// } |
446 | | /// } |
447 | | /// |
448 | | /// writeable::impl_display_with_writeable!(Demo); |
449 | | /// |
450 | | /// assert_writeable_eq!(&Demo, "foo"); |
451 | | /// assert_writeable_eq!(&Demo, "foo", "Message: {}", "Hello World"); |
452 | | /// |
453 | | /// assert_writeable_parts_eq!(&Demo, "foo", [(0, 3, WORD)]); |
454 | | /// assert_writeable_parts_eq!( |
455 | | /// &Demo, |
456 | | /// "foo", |
457 | | /// [(0, 3, WORD)], |
458 | | /// "Message: {}", |
459 | | /// "Hello World" |
460 | | /// ); |
461 | | /// ``` |
462 | | /// |
463 | | /// [`*_parts_eq`]: assert_writeable_parts_eq |
464 | | #[macro_export] |
465 | | #[cfg(feature = "alloc")] |
466 | | macro_rules! assert_writeable_eq { |
467 | | ($actual_writeable:expr, $expected_str:expr $(,)?) => { |
468 | | $crate::assert_writeable_eq!($actual_writeable, $expected_str, "") |
469 | | }; |
470 | | ($actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{ |
471 | | $crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*); |
472 | | }}; |
473 | | (@internal, $actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{ |
474 | | let actual_writeable = &$actual_writeable; |
475 | | let (actual_str, actual_parts) = $crate::_internal::writeable_to_parts_for_test(actual_writeable); |
476 | | let actual_len = actual_str.len(); |
477 | | assert_eq!(actual_str, $expected_str, $($arg)*); |
478 | | let cow = $crate::Writeable::write_to_string(actual_writeable); |
479 | | assert_eq!(actual_str, cow, $($arg)+); |
480 | | if let Some(borrowed) = ($crate::Writeable::writeable_borrow(&actual_writeable)) { |
481 | | assert_eq!(borrowed, $expected_str, $($arg)*); |
482 | | assert!(matches!(cow, std::borrow::Cow::Borrowed(_)), $($arg)*); |
483 | | } |
484 | | let length_hint = $crate::Writeable::writeable_length_hint(actual_writeable); |
485 | | let lower = length_hint.0; |
486 | | assert!( |
487 | | lower <= actual_len, |
488 | | "hint lower bound {lower} larger than actual length {actual_len}: {}", |
489 | | format!($($arg)*), |
490 | | ); |
491 | | if let Some(upper) = length_hint.1 { |
492 | | assert!( |
493 | | actual_len <= upper, |
494 | | "hint upper bound {upper} smaller than actual length {actual_len}: {}", |
495 | | format!($($arg)*), |
496 | | ); |
497 | | } |
498 | | assert_eq!(actual_writeable.to_string(), $expected_str, $($arg)*); |
499 | | actual_parts // return for assert_writeable_parts_eq |
500 | | }}; |
501 | | } |
502 | | |
503 | | /// See [`assert_writeable_eq`]. |
504 | | #[macro_export] |
505 | | #[cfg(feature = "alloc")] |
506 | | macro_rules! assert_writeable_parts_eq { |
507 | | ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => { |
508 | | $crate::assert_writeable_parts_eq!($actual_writeable, $expected_str, $expected_parts, "") |
509 | | }; |
510 | | ($actual_writeable:expr, $expected_str:expr, $expected_parts:expr, $($arg:tt)+) => {{ |
511 | | let actual_parts = $crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*); |
512 | | assert_eq!(actual_parts, $expected_parts, $($arg)+); |
513 | | }}; |
514 | | } |