1use core::fmt::{self, Write as _};
7
8#[cfg(feature = "alloc")]
9use alloc::collections::TryReserveError;
10#[cfg(all(feature = "alloc", not(feature = "std")))]
11use alloc::string::String;
12
13#[derive(Debug, Clone, Copy)]
15pub struct CapacityOverflowError;
16
17impl fmt::Display for CapacityOverflowError {
18 #[inline]
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 f.write_str("buffer capacity overflow")
21 }
22}
23
24#[cfg(feature = "std")]
25impl std::error::Error for CapacityOverflowError {}
26
27struct ByteBufWriter<'b> {
29 buffer: &'b mut [u8],
31 len_written: usize,
33}
34
35impl fmt::Write for ByteBufWriter<'_> {
36 fn write_str(&mut self, s: &str) -> fmt::Result {
37 let len = s.len();
38 if self.buffer.len() < len {
40 return Err(fmt::Error);
41 }
42 let (prefix, rest) = core::mem::take(&mut self.buffer).split_at_mut(len);
43 prefix.copy_from_slice(s.as_bytes());
44 self.buffer = rest;
45 self.len_written += len;
46 Ok(())
47 }
48}
49
50pub fn write_to_slice<'a, T: fmt::Display>(
52 buf: &'a mut [u8],
53 value: &T,
54) -> Result<&'a str, CapacityOverflowError> {
55 let mut writer = ByteBufWriter {
56 buffer: buf,
57 len_written: 0,
58 };
59 if write!(writer, "{}", value).is_err() {
60 return Err(CapacityOverflowError);
61 }
62 let len = writer.len_written;
63 let result =
64 core::str::from_utf8(&buf[..len]).expect("fmt::Display writes valid UTF-8 byte sequence");
65 Ok(result)
66}
67
68#[cfg(feature = "alloc")]
70struct StringWriter<'a> {
71 buffer: &'a mut String,
73 error: Option<TryReserveError>,
75}
76
77#[cfg(feature = "alloc")]
78impl fmt::Write for StringWriter<'_> {
79 fn write_str(&mut self, s: &str) -> fmt::Result {
80 if self.error.is_some() {
81 return Err(fmt::Error);
82 }
83 if let Err(e) = self.buffer.try_reserve(s.len()) {
84 self.error = Some(e);
85 return Err(fmt::Error);
86 }
87 self.buffer.push_str(s);
89 Ok(())
90 }
91}
92
93#[cfg(feature = "alloc")]
98pub fn try_append_to_string<T: fmt::Display>(
99 dest: &mut String,
100 value: &T,
101) -> Result<(), TryReserveError> {
102 let mut writer = StringWriter {
103 buffer: dest,
104 error: None,
105 };
106 if write!(writer, "{}", value).is_err() {
107 let e = writer
108 .error
109 .expect("allocation error should be set on formatting failure");
110 return Err(e);
111 }
112 Ok(())
113}
114
115pub(crate) fn eq_str_display<T>(s: &str, d: &T) -> bool
117where
118 T: ?Sized + fmt::Display,
119{
120 struct CmpWriter<'a>(&'a str);
122 impl fmt::Write for CmpWriter<'_> {
123 fn write_str(&mut self, s: &str) -> fmt::Result {
124 if let Some(rest) = self.0.strip_prefix(s) {
125 self.0 = rest;
126 Ok(())
127 } else {
128 Err(fmt::Error)
129 }
130 }
131 }
132
133 let mut writer = CmpWriter(s);
134 let succeeded = write!(writer, "{}", d).is_ok();
135 succeeded && writer.0.is_empty()
136}
137
138#[derive(Clone, Copy)]
140pub(crate) struct Censored;
141
142impl core::fmt::Debug for Censored {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
144 f.write_str("{censored}")
145 }
146}
147
148#[cfg(feature = "alloc")]
150pub trait ToStringFallible: alloc::string::ToString {
151 fn try_to_string(&self) -> Result<String, TryReserveError>;
153}
154
155#[cfg(feature = "alloc")]
156impl<T: fmt::Display> ToStringFallible for T {
157 #[inline]
159 fn try_to_string(&self) -> Result<String, TryReserveError> {
160 let mut buf = String::new();
161 try_append_to_string(&mut buf, self)?;
162 Ok(buf)
163 }
164}
165
166#[cfg(feature = "alloc")]
168pub trait ToDedicatedString {
169 type Target;
171
172 fn try_to_dedicated_string(&self) -> Result<Self::Target, TryReserveError>;
174
175 #[inline]
181 #[must_use]
182 fn to_dedicated_string(&self) -> Self::Target {
183 self.try_to_dedicated_string()
184 .expect("failed to allocate enough memory")
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn eq_str_display_1() {
194 assert!(eq_str_display("hello", "hello"));
195 assert!(eq_str_display("42", &42));
196
197 assert!(eq_str_display(
198 r#"\x00\t\r\n\xff\\"#,
199 &b"\x00\t\r\n\xff\\".escape_ascii()
200 ));
201
202 assert!(!eq_str_display("hello", "world"));
203 assert!(!eq_str_display("hello world", "hello"));
204 assert!(!eq_str_display("hello", "hello world"));
205 assert!(!eq_str_display("42", &4));
206 assert!(!eq_str_display("4", &42));
207 }
208}