writebuf_core/
lib.rs

1//! A writeable buffer that implements [`fmt::Write`] or [`ufmt::uWrite`](https://docs.rs/ufmt/latest/ufmt/trait.uWrite.html).
2//!
3//! # Example
4//! ```
5//! use writebuf_core::WriteBuf;
6//! use ufmt::{uwrite, uWrite};
7//!
8//! // write to buffer
9//! let mut buf: WriteBuf<10> = WriteBuf::from("123");
10//! uwrite!(&mut buf, "{}", "456").ok();
11//! uwrite!(&mut buf, "{}", 789).ok();
12//! buf.write_str("0").ok();
13//! buf.write_str("E").err();
14//!
15//! // convert to ASCII string
16//! buf.into_ascii_lossy().as_str();
17//! ```
18//!
19//! # ufmt
20//! ufmt is more compact than core::fmt. By default, ufmt feature is enabled.
21
22#![cfg_attr(not(test), no_std)]
23
24use heapless::{Vec, String};
25
26#[cfg(not(feature = "ufmt"))]
27use core::fmt;
28use core::ops::{Deref, DerefMut};
29
30#[cfg(feature = "ufmt")]
31use ufmt::uWrite;
32
33#[derive(Default, Clone, Debug)]
34pub struct WriteBuf<const N: usize> {
35    buffer: Vec<u8, N>,
36}
37
38impl<const N: usize> WriteBuf<N> {
39    pub const fn new() -> Self {
40        Self {
41            buffer: Vec::new()
42        }
43    }
44
45    /// Try convert to UTF-8 str
46    pub fn to_str(&self) -> Result<&str, ()> {
47        core::str::from_utf8(self.buffer.as_slice()).map_err(|_e| ())
48    }
49
50    /// Convert to ASCII string by replacing any invalid characters with `~`
51    pub fn into_ascii_lossy(self) -> String<N> {
52        let mut s = String::<N>::new();
53        for &c in self.iter() {
54            if c >= 0x80 {
55                s.push('~').ok();
56            } else {
57                s.push(c as char).ok();
58            }
59        }
60        s
61    }
62}
63
64impl<T: AsRef<[u8]>, const N: usize> From<T> for WriteBuf<N> {
65    fn from(value: T) -> Self {
66        let data = value.as_ref();
67        let mut buf = Self::new();
68        buf.extend_from_slice(&data[..core::cmp::min(data.len(), N)]).ok();
69        buf
70    }
71}
72
73impl<const N: usize> Deref for WriteBuf<N> {
74    type Target = Vec<u8, N>;
75
76    fn deref(&self) -> &Self::Target {
77        &self.buffer
78    }
79}
80
81impl<const N: usize> DerefMut for WriteBuf<N> {
82    fn deref_mut(&mut self) -> &mut Self::Target {
83        &mut self.buffer
84    }
85}
86
87#[cfg(not(feature = "ufmt"))]
88impl<const N: usize> fmt::Write for WriteBuf<N> {
89    fn write_str(&mut self, s: &str) -> fmt::Result {
90        self.buffer.write_str(s)
91    }
92}
93
94#[cfg(feature = "ufmt")]
95impl<const N: usize> uWrite for WriteBuf<N> {
96    type Error = ();
97
98    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
99        self.buffer.write_str(s)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_write() {
109        let buf = WriteBuf::<10>::from("1000");
110        assert_eq!(buf.to_str().unwrap(), "1000");
111    }
112
113    #[test]
114    fn test_full() {
115        let mut buf = WriteBuf::<10>::from("123456789");
116        buf.write_str("abc").err();
117        assert_eq!(buf.to_str().unwrap(), "123456789");
118    }
119
120    #[test]
121    fn test_into_ascii_lossy() {
122        let mut buf = WriteBuf::<10>::from("123456789");
123        buf.resize_default(10).ok();
124        buf[9] = 0x80u8;
125        assert_eq!(buf.into_ascii_lossy(), "123456789~");
126    }
127}