writeable/concat.rs
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
5use crate::TryWriteable;
6use crate::Writeable;
7use core::fmt;
8
9/// A [`Writeable`] that efficiently concatenates two other [`Writeable`]s.
10///
11/// See the [`concat_writeable!`] macro for a convenient way to make one of these.
12///
13/// # Examples
14///
15/// ```
16/// use writeable::adapters::Concat;
17/// use writeable::assert_writeable_eq;
18///
19/// assert_writeable_eq!(Concat("Number: ", 25), "Number: 25");
20/// ```
21///
22/// With [`TryWriteable`]:
23///
24/// ```
25/// use writeable::adapters::Concat;
26/// use writeable::assert_try_writeable_eq;
27/// use writeable::TryWriteable;
28///
29/// struct AlwaysPanic;
30///
31/// impl TryWriteable for AlwaysPanic {
32/// type Error = &'static str;
33/// fn try_write_to_parts<W: writeable::PartsWrite + ?Sized>(
34/// &self,
35/// _sink: &mut W,
36/// ) -> Result<Result<(), Self::Error>, core::fmt::Error> {
37/// // Unreachable panic: the first Writeable errors,
38/// // so the second Writeable is not evaluated.
39/// panic!("this is a test to demonstrate unreachable code")
40/// }
41/// }
42///
43/// let writeable0: Result<&str, &str> = Err("error message");
44/// let writeable1 = AlwaysPanic;
45///
46/// assert_try_writeable_eq!(
47/// Concat(writeable0, writeable1),
48/// "error message",
49/// Err("error message"),
50/// )
51/// ```
52#[derive(Debug)]
53#[allow(clippy::exhaustive_structs)] // designed for nesting
54pub struct Concat<A, B>(pub A, pub B);
55
56impl<A, B> Writeable for Concat<A, B>
57where
58 A: Writeable,
59 B: Writeable,
60{
61 #[inline]
62 fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
63 self.0.write_to(sink)?;
64 self.1.write_to(sink)
65 }
66 #[inline]
67 fn write_to_parts<S: crate::PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
68 self.0.write_to_parts(sink)?;
69 self.1.write_to_parts(sink)
70 }
71 #[inline]
72 fn writeable_length_hint(&self) -> crate::LengthHint {
73 self.0.writeable_length_hint() + self.1.writeable_length_hint()
74 }
75}
76
77impl<A, B, E> TryWriteable for Concat<A, B>
78where
79 A: TryWriteable<Error = E>,
80 B: TryWriteable<Error = E>,
81{
82 type Error = E;
83 #[inline]
84 fn try_write_to<W: fmt::Write + ?Sized>(
85 &self,
86 sink: &mut W,
87 ) -> Result<Result<(), Self::Error>, fmt::Error> {
88 if let Err(e) = self.0.try_write_to(sink)? {
89 return Ok(Err(e));
90 }
91 self.1.try_write_to(sink)
92 }
93 #[inline]
94 fn try_write_to_parts<S: crate::PartsWrite + ?Sized>(
95 &self,
96 sink: &mut S,
97 ) -> Result<Result<(), Self::Error>, fmt::Error> {
98 if let Err(e) = self.0.try_write_to_parts(sink)? {
99 return Ok(Err(e));
100 }
101 self.1.try_write_to_parts(sink)
102 }
103 #[inline]
104 fn writeable_length_hint(&self) -> crate::LengthHint {
105 self.0.writeable_length_hint() + self.1.writeable_length_hint()
106 }
107}
108
109impl<A, B> fmt::Display for Concat<A, B>
110where
111 A: Writeable,
112 B: Writeable,
113{
114 #[inline]
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 self.0.write_to(f)?;
117 self.1.write_to(f)
118 }
119}
120
121/// Returns a [`Writeable`] concatenating any number of [`Writeable`]s.
122///
123/// The macro resolves to a nested [`Concat`].
124///
125/// # Examples
126///
127/// ```
128/// use writeable::assert_writeable_eq;
129///
130/// let concatenated = writeable::concat_writeable!("Health: ", 5, '/', 8);
131///
132/// assert_writeable_eq!(concatenated, "Health: 5/8");
133/// ```
134#[macro_export]
135#[doc(hidden)] // macro
136macro_rules! __concat_writeable {
137 // Base case:
138 ($x:expr) => ($x);
139 // `$x` followed by at least one `$y,`
140 ($x:expr, $($y:expr),+) => (
141 // Call `concat_writeable!` recursively on the tail `$y`
142 $crate::adapters::Concat($x, $crate::concat_writeable!($($y),+))
143 )
144}
145#[doc(inline)]
146pub use __concat_writeable as concat_writeable;