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 cursor: usize,
33}
34
35impl fmt::Write for ByteBufWriter<'_> {
36 fn write_str(&mut self, s: &str) -> fmt::Result {
37 let dest = &mut self.buffer[self.cursor..];
38 if dest.len() < s.len() {
39 return Err(fmt::Error);
40 }
41 dest[..s.len()].copy_from_slice(s.as_bytes());
42 self.cursor += s.len();
43 Ok(())
44 }
45}
46
47pub fn write_to_slice<'a, T: fmt::Display>(
49 buf: &'a mut [u8],
50 value: &T,
51) -> Result<&'a str, CapacityOverflowError> {
52 let mut writer = ByteBufWriter {
53 buffer: buf,
54 cursor: 0,
55 };
56 if write!(writer, "{}", value).is_err() {
57 return Err(CapacityOverflowError);
58 }
59 let len = writer.cursor;
60 let result = core::str::from_utf8(&buf[..len])
61 .expect("[validity] fmt::Display writes valid UTF-8 byte sequence");
62 Ok(result)
63}
64
65#[cfg(feature = "alloc")]
67struct StringWriter<'a> {
68 buffer: &'a mut String,
70 error: Option<TryReserveError>,
72}
73
74#[cfg(feature = "alloc")]
75impl fmt::Write for StringWriter<'_> {
76 fn write_str(&mut self, s: &str) -> fmt::Result {
77 if self.error.is_some() {
78 return Err(fmt::Error);
79 }
80 if let Err(e) = self.buffer.try_reserve(s.len()) {
81 self.error = Some(e);
82 return Err(fmt::Error);
83 }
84 self.buffer.push_str(s);
86 Ok(())
87 }
88}
89
90#[cfg(feature = "alloc")]
95pub fn try_append_to_string<T: fmt::Display>(
96 dest: &mut String,
97 value: &T,
98) -> Result<(), TryReserveError> {
99 let mut writer = StringWriter {
100 buffer: dest,
101 error: None,
102 };
103 if write!(writer, "{}", value).is_err() {
104 let e = writer
105 .error
106 .expect("[consistency] allocation error should be set on formatting failure");
107 return Err(e);
108 }
109 Ok(())
110}
111
112pub(crate) fn eq_str_display<T>(s: &str, d: &T) -> bool
114where
115 T: ?Sized + fmt::Display,
116{
117 struct CmpWriter<'a>(&'a str);
119 impl fmt::Write for CmpWriter<'_> {
120 fn write_str(&mut self, s: &str) -> fmt::Result {
121 if self.0.len() < s.len() {
122 return Err(fmt::Error);
123 }
124 let (prefix, rest) = self.0.split_at(s.len());
125 self.0 = rest;
126 if prefix == s {
127 Ok(())
128 } else {
129 Err(fmt::Error)
130 }
131 }
132 }
133
134 let mut writer = CmpWriter(s);
135 let succeeded = write!(writer, "{}", d).is_ok();
136 succeeded && writer.0.is_empty()
137}
138
139#[derive(Clone, Copy)]
141pub(crate) struct Censored;
142
143impl core::fmt::Debug for Censored {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
145 f.write_str("{censored}")
146 }
147}
148
149#[cfg(feature = "alloc")]
151pub trait ToStringFallible: alloc::string::ToString {
152 fn try_to_string(&self) -> Result<String, TryReserveError>;
154}
155
156#[cfg(feature = "alloc")]
157impl<T: fmt::Display> ToStringFallible for T {
158 #[inline]
160 fn try_to_string(&self) -> Result<String, TryReserveError> {
161 let mut buf = String::new();
162 try_append_to_string(&mut buf, self)?;
163 Ok(buf)
164 }
165}
166
167#[cfg(feature = "alloc")]
169pub trait ToDedicatedString {
170 type Target;
172
173 fn try_to_dedicated_string(&self) -> Result<Self::Target, TryReserveError>;
175
176 #[inline]
182 #[must_use]
183 fn to_dedicated_string(&self) -> Self::Target {
184 self.try_to_dedicated_string()
185 .expect("failed to allocate enough memory")
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn eq_str_display_1() {
195 assert!(eq_str_display("hello", "hello"));
196 assert!(eq_str_display("42", &42));
197
198 assert!(eq_str_display(
199 r#"\x00\t\r\n\xff\\"#,
200 &b"\x00\t\r\n\xff\\".escape_ascii()
201 ));
202
203 assert!(!eq_str_display("hello", "world"));
204 assert!(!eq_str_display("hello world", "hello"));
205 assert!(!eq_str_display("hello", "hello world"));
206 assert!(!eq_str_display("42", &4));
207 assert!(!eq_str_display("4", &42));
208 }
209}