facet_dom/serializer/
write_scalar.rs

1//! Scalar value writing for DOM serializers.
2
3extern crate alloc;
4
5use alloc::string::String;
6use core::fmt::Write as FmtWrite;
7use facet_core::{Def, ScalarType};
8use facet_reflect::Peek;
9
10use super::DomSerializer;
11
12/// Extension trait for writing scalar values directly to output.
13pub trait WriteScalar: DomSerializer {
14    /// Format a scalar value to a string (for attributes).
15    ///
16    /// Returns `Some(string)` if the value is a scalar, `None` otherwise.
17    fn format_scalar(&self, value: Peek<'_, '_>) -> Option<String> {
18        // Handle Option<T> by unwrapping if Some
19        if let Def::Option(_) = &value.shape().def
20            && let Ok(opt) = value.into_option()
21        {
22            return match opt.value() {
23                Some(inner) => self.format_scalar(inner),
24                None => None,
25            };
26        }
27
28        if let Some(scalar_type) = value.scalar_type() {
29            let mut buf = ScalarBuffer::new();
30            let written = match scalar_type {
31                ScalarType::Unit => {
32                    buf.push_str("null");
33                    true
34                }
35                ScalarType::Bool => {
36                    if let Ok(b) = value.get::<bool>() {
37                        buf.push_str(if *b { "true" } else { "false" });
38                        true
39                    } else {
40                        false
41                    }
42                }
43                ScalarType::Char => {
44                    if let Ok(c) = value.get::<char>() {
45                        buf.push(*c);
46                        true
47                    } else {
48                        false
49                    }
50                }
51                ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
52                    if let Some(s) = value.as_str() {
53                        return Some(s.to_string());
54                    }
55                    false
56                }
57                ScalarType::F32 => {
58                    if let Ok(v) = value.get::<f32>() {
59                        self.write_float(*v as f64, &mut buf);
60                        true
61                    } else {
62                        false
63                    }
64                }
65                ScalarType::F64 => {
66                    if let Ok(v) = value.get::<f64>() {
67                        self.write_float(*v, &mut buf);
68                        true
69                    } else {
70                        false
71                    }
72                }
73                ScalarType::U8 => write_int!(buf, value, u8),
74                ScalarType::U16 => write_int!(buf, value, u16),
75                ScalarType::U32 => write_int!(buf, value, u32),
76                ScalarType::U64 => write_int!(buf, value, u64),
77                ScalarType::U128 => write_int!(buf, value, u128),
78                ScalarType::USize => write_int!(buf, value, usize),
79                ScalarType::I8 => write_int!(buf, value, i8),
80                ScalarType::I16 => write_int!(buf, value, i16),
81                ScalarType::I32 => write_int!(buf, value, i32),
82                ScalarType::I64 => write_int!(buf, value, i64),
83                ScalarType::I128 => write_int!(buf, value, i128),
84                ScalarType::ISize => write_int!(buf, value, isize),
85                #[cfg(feature = "net")]
86                ScalarType::IpAddr => write_int!(buf, value, core::net::IpAddr),
87                #[cfg(feature = "net")]
88                ScalarType::Ipv4Addr => write_int!(buf, value, core::net::Ipv4Addr),
89                #[cfg(feature = "net")]
90                ScalarType::Ipv6Addr => write_int!(buf, value, core::net::Ipv6Addr),
91                #[cfg(feature = "net")]
92                ScalarType::SocketAddr => write_int!(buf, value, core::net::SocketAddr),
93                _ => false,
94            };
95
96            if written {
97                return Some(buf.as_str().to_string());
98            }
99        }
100
101        // Try Display for Def::Scalar types (SmolStr, etc.)
102        if matches!(value.shape().def, Def::Scalar) && value.shape().vtable.has_display() {
103            let mut buf = ScalarBuffer::new();
104            let _ = write!(buf, "{}", value);
105            return Some(buf.as_str().to_string());
106        }
107
108        None
109    }
110
111    /// Write a scalar value to the serializer's output.
112    ///
113    /// Returns `Ok(true)` if the value was written, `Ok(false)` if not a scalar.
114    /// Override to customize formatting (e.g., custom float precision).
115    fn write_scalar(&mut self, value: Peek<'_, '_>) -> Result<bool, Self::Error> {
116        // Handle Option<T> by unwrapping if Some
117        if let Def::Option(_) = &value.shape().def
118            && let Ok(opt) = value.into_option()
119        {
120            return match opt.value() {
121                Some(inner) => self.write_scalar(inner),
122                None => Ok(false),
123            };
124        }
125
126        if let Some(scalar_type) = value.scalar_type() {
127            let mut buf = ScalarBuffer::new();
128            let written = match scalar_type {
129                ScalarType::Unit => {
130                    buf.push_str("null");
131                    true
132                }
133                ScalarType::Bool => {
134                    if let Ok(b) = value.get::<bool>() {
135                        buf.push_str(if *b { "true" } else { "false" });
136                        true
137                    } else {
138                        false
139                    }
140                }
141                ScalarType::Char => {
142                    if let Ok(c) = value.get::<char>() {
143                        buf.push(*c);
144                        true
145                    } else {
146                        false
147                    }
148                }
149                ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
150                    if let Some(s) = value.as_str() {
151                        self.text(s)?;
152                        return Ok(true);
153                    }
154                    false
155                }
156                ScalarType::F32 => {
157                    if let Ok(v) = value.get::<f32>() {
158                        self.write_float(*v as f64, &mut buf);
159                        true
160                    } else {
161                        false
162                    }
163                }
164                ScalarType::F64 => {
165                    if let Ok(v) = value.get::<f64>() {
166                        self.write_float(*v, &mut buf);
167                        true
168                    } else {
169                        false
170                    }
171                }
172                ScalarType::U8 => write_int!(buf, value, u8),
173                ScalarType::U16 => write_int!(buf, value, u16),
174                ScalarType::U32 => write_int!(buf, value, u32),
175                ScalarType::U64 => write_int!(buf, value, u64),
176                ScalarType::U128 => write_int!(buf, value, u128),
177                ScalarType::USize => write_int!(buf, value, usize),
178                ScalarType::I8 => write_int!(buf, value, i8),
179                ScalarType::I16 => write_int!(buf, value, i16),
180                ScalarType::I32 => write_int!(buf, value, i32),
181                ScalarType::I64 => write_int!(buf, value, i64),
182                ScalarType::I128 => write_int!(buf, value, i128),
183                ScalarType::ISize => write_int!(buf, value, isize),
184                #[cfg(feature = "net")]
185                ScalarType::IpAddr => write_display!(buf, value, core::net::IpAddr),
186                #[cfg(feature = "net")]
187                ScalarType::Ipv4Addr => write_display!(buf, value, core::net::Ipv4Addr),
188                #[cfg(feature = "net")]
189                ScalarType::Ipv6Addr => write_display!(buf, value, core::net::Ipv6Addr),
190                #[cfg(feature = "net")]
191                ScalarType::SocketAddr => write_display!(buf, value, core::net::SocketAddr),
192                _ => false,
193            };
194
195            if written {
196                self.text(buf.as_str())?;
197                return Ok(true);
198            }
199        }
200
201        // Try Display for Def::Scalar types (SmolStr, etc.)
202        if matches!(value.shape().def, Def::Scalar) && value.shape().vtable.has_display() {
203            let mut buf = ScalarBuffer::new();
204            let _ = write!(buf, "{}", value);
205            self.text(buf.as_str())?;
206            return Ok(true);
207        }
208
209        Ok(false)
210    }
211
212    /// Write a float value. Override to customize float formatting.
213    fn write_float(&self, value: f64, buf: &mut ScalarBuffer) {
214        let _ = write!(buf, "{}", value);
215    }
216}
217
218// Blanket implementation for all DomSerializers
219impl<T: DomSerializer> WriteScalar for T {}
220
221macro_rules! write_int {
222    ($buf:expr, $value:expr, $ty:ty) => {{
223        if let Ok(v) = $value.get::<$ty>() {
224            let _ = write!($buf, "{}", v);
225            true
226        } else {
227            false
228        }
229    }};
230}
231use write_int;
232
233#[cfg(feature = "net")]
234macro_rules! write_display {
235    ($buf:expr, $value:expr, $ty:ty) => {{
236        if let Ok(v) = $value.get::<$ty>() {
237            let _ = write!($buf, "{}", v);
238            true
239        } else {
240            false
241        }
242    }};
243}
244#[cfg(feature = "net")]
245use write_display;
246
247/// Buffer for formatting scalar values without heap allocation for small values.
248/// Uses a small inline buffer, falling back to heap for larger values.
249pub struct ScalarBuffer {
250    // Inline buffer for small values (covers most integers, small floats)
251    inline: [u8; 32],
252    len: usize,
253    // Overflow to heap if needed
254    overflow: Option<String>,
255}
256
257impl ScalarBuffer {
258    /// Create a new empty buffer.
259    pub fn new() -> Self {
260        Self {
261            inline: [0u8; 32],
262            len: 0,
263            overflow: None,
264        }
265    }
266
267    /// Get the buffer contents as a string slice.
268    pub fn as_str(&self) -> &str {
269        if let Some(ref s) = self.overflow {
270            s.as_str()
271        } else {
272            // Safety: we only write valid UTF-8 via fmt::Write
273            unsafe { core::str::from_utf8_unchecked(&self.inline[..self.len]) }
274        }
275    }
276
277    fn push_str(&mut self, s: &str) {
278        if let Some(ref mut overflow) = self.overflow {
279            overflow.push_str(s);
280        } else if self.len + s.len() <= self.inline.len() {
281            self.inline[self.len..self.len + s.len()].copy_from_slice(s.as_bytes());
282            self.len += s.len();
283        } else {
284            // Overflow to heap
285            let mut heap = String::with_capacity(self.len + s.len() + 32);
286            heap.push_str(unsafe { core::str::from_utf8_unchecked(&self.inline[..self.len]) });
287            heap.push_str(s);
288            self.overflow = Some(heap);
289        }
290    }
291
292    fn push(&mut self, c: char) {
293        let mut buf = [0u8; 4];
294        let s = c.encode_utf8(&mut buf);
295        self.push_str(s);
296    }
297}
298
299impl Default for ScalarBuffer {
300    fn default() -> Self {
301        Self::new()
302    }
303}
304
305impl FmtWrite for ScalarBuffer {
306    fn write_str(&mut self, s: &str) -> core::fmt::Result {
307        self.push_str(s);
308        Ok(())
309    }
310}