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