Skip to main content

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