1#![cfg_attr(not(test), no_std)]
2#![deny(missing_docs)]
3
4use core::fmt::{self, Display, Formatter};
20
21#[derive(Debug, Clone, Copy)]
23pub struct WriteBuffer<const N: usize> {
24 buffer: [u8; N],
25 cursor: usize,
26}
27
28impl<const N: usize> WriteBuffer<N> {
29 pub fn new() -> Self {
31 let buf = [0u8; N];
32 WriteBuffer {
33 buffer: buf,
34 cursor: 0,
35 }
36 }
37
38 pub fn as_slice(&self) -> &[u8] {
40 &self.buffer[..self.cursor]
41 }
42
43 fn as_slice_mut(&mut self) -> &mut [u8] {
50 &mut self.buffer[..self.cursor]
51 }
52
53 pub fn reset(&mut self) {
58 self.cursor = 0;
59 }
60
61 pub fn as_str(&self) -> &str {
63 unsafe { core::str::from_utf8_unchecked(self.as_slice()) }
67 }
68
69 pub fn as_str_mut(&mut self) -> &mut str {
71 unsafe { core::str::from_utf8_unchecked_mut(self.as_slice_mut()) }
75 }
76
77 pub fn len(&self) -> usize {
79 self.cursor
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.len() == 0
85 }
86
87 pub fn remaining(&self) -> usize {
89 N - self.len()
90 }
91
92 pub fn is_full(&self) -> bool {
94 self.remaining() == 0
95 }
96}
97
98impl<const N: usize> Default for WriteBuffer<N> {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104impl<const N: usize> PartialEq for WriteBuffer<N> {
105 fn eq(&self, other: &Self) -> bool {
106 self.as_slice() == other.as_slice()
107 }
108}
109
110impl<const N: usize> core::hash::Hash for WriteBuffer<N> {
111 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
112 self.as_slice().hash(state);
113 }
114}
115
116impl<const N: usize> Eq for WriteBuffer<N> {}
117
118impl<const N: usize> fmt::Write for WriteBuffer<N> {
119 fn write_str(&mut self, s: &str) -> fmt::Result {
120 let bytes = s.as_bytes();
121
122 let new_cursor = self.cursor + bytes.len();
124
125 if new_cursor > N {
127 return Err(fmt::Error);
128 }
129
130 self.buffer[self.cursor..new_cursor].copy_from_slice(bytes);
132
133 self.cursor = new_cursor;
135
136 Ok(())
137 }
138}
139
140impl<const N: usize> Display for WriteBuffer<N> {
141 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
142 write!(f, "{}", self.as_str())
143 }
144}
145
146#[cfg(test)]
147mod test {
148 use super::WriteBuffer;
149 use core::fmt::Write;
150
151 #[test]
152 fn test_write_wrapper() {
153 let x = 123;
154
155 let mut buffer: WriteBuffer<20> = WriteBuffer::new();
156 write!(buffer, "{}", x).unwrap();
157 assert_eq!(&buffer.as_slice()[0..3], b"123");
158
159 buffer.reset();
160 let x = 2.242424;
161 write!(buffer, "{:.2}", x).unwrap();
162 assert_eq!(buffer.as_slice(), b"2.24");
163
164 buffer.reset();
165 let x = 20;
166 write!(buffer, "Longer than {} characters sentence", x).unwrap_err();
167
168 buffer.reset();
169 write!(buffer, "{}", "1").unwrap();
170 write!(buffer, "{}", "2").unwrap();
171 assert_eq!(buffer.as_slice(), b"12");
172 }
173
174 #[test]
175 fn test_display() {
176 let x = 123;
177
178 let mut buffer: WriteBuffer<20> = WriteBuffer::new();
179 write!(buffer, "{}", x).unwrap();
180 assert_eq!("123", format!("{}", buffer));
181 }
182
183 #[test]
184 fn test_as_str_mut() {
185 let mut buffer: WriteBuffer<20> = WriteBuffer::new();
186 write!(buffer, "hello world").unwrap();
187 buffer.as_str_mut().make_ascii_uppercase();
188
189 assert_eq!(buffer.as_str(), "HELLO WORLD");
190 }
191
192 #[test]
193 fn test_is_empty_is_full_and_overflow() {
194 let mut buffer: WriteBuffer<10> = WriteBuffer::new();
195 assert!(buffer.is_empty());
196 write!(buffer, "0123456789").unwrap();
197 assert!(buffer.is_full());
198 assert_eq!(write!(buffer, "!"), Err(core::fmt::Error));
199 }
200}