• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 14560074154

20 Apr 2025 01:43PM UTC coverage: 44.989% (+0.6%) from 44.351%
14560074154

Pull #296

github

web-flow
Merge bdeb079fa into c7c4c16b6
Pull Request #296: Implement the skip serializing attribute

46 of 54 new or added lines in 6 files covered. (85.19%)

16 existing lines in 2 files now uncovered.

5050 of 11225 relevant lines covered (44.99%)

50.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.96
/facet-json/src/serialize.rs
1
use core::num::NonZero;
2
use facet_core::{Def, Facet, FieldAttribute};
3
use facet_reflect::Peek;
4
use std::io::{self, Write};
5

6
/// Serializes a value to JSON
7
pub fn to_string<T: Facet>(value: &T) -> String {
21✔
8
    let peek = Peek::new(value);
21✔
9
    let mut output = Vec::new();
21✔
10
    serialize(&peek, &mut output).unwrap();
21✔
11
    String::from_utf8(output).unwrap()
21✔
12
}
21✔
13

14
/// Serializes a Peek instance to JSON
15
pub fn peek_to_string(peek: &Peek<'_>) -> String {
1✔
16
    let mut output = Vec::new();
1✔
17
    serialize(peek, &mut output).unwrap();
1✔
18
    String::from_utf8(output).unwrap()
1✔
19
}
1✔
20

21
/// Serializes a value to a writer in JSON format
22
pub fn to_writer<T: Facet, W: Write>(value: &T, writer: &mut W) -> io::Result<()> {
1✔
23
    let peek = Peek::new(value);
1✔
24
    serialize(&peek, writer)
1✔
25
}
1✔
26

27
/// Serializes a Peek instance to a writer in JSON format
28
pub fn peek_to_writer<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
1✔
29
    serialize(peek, writer)
1✔
30
}
1✔
31

32
/// The core serialization function
33
fn serialize<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
72✔
34
    match peek.shape().def {
72✔
35
        Def::Scalar(_) => serialize_scalar(peek, writer),
37✔
36
        Def::Struct(_) => serialize_struct(peek, writer),
24✔
37
        Def::List(_) => serialize_list(peek, writer),
2✔
38
        Def::Map(_) => serialize_map(peek, writer),
2✔
39
        Def::Enum(_) => serialize_enum(peek, writer),
1✔
40
        Def::Option(_) => serialize_option(peek, writer),
6✔
41
        _ => Err(io::Error::new(
×
42
            io::ErrorKind::Other,
×
43
            format!("Unsupported type: {}", peek.shape()),
×
44
        )),
×
45
    }
46
}
72✔
47

48
/// Serializes a scalar value to JSON
49
fn serialize_scalar<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
37✔
50
    // Handle basic scalar types
37✔
51
    if peek.shape().is_type::<bool>() {
37✔
52
        let value = peek.get::<bool>().unwrap();
×
53
        write!(writer, "{}", if *value { "true" } else { "false" })
×
54
    } else if peek.shape().is_type::<String>() {
37✔
55
        let value = peek.get::<String>().unwrap();
20✔
56
        write_json_string(writer, value)
20✔
57
    } else if peek.shape().is_type::<&str>() {
17✔
58
        let value = peek.get::<&str>().unwrap();
8✔
59
        write_json_string(writer, value)
8✔
60
    } else if peek.shape().is_type::<alloc::borrow::Cow<'_, str>>() {
9✔
61
        let value = peek.get::<alloc::borrow::Cow<'_, str>>().unwrap();
1✔
62
        write_json_string(writer, value)
1✔
63
    }
64
    // Integer types
65
    else if peek.shape().is_type::<u8>() {
8✔
66
        let value = peek.get::<u8>().unwrap();
×
67
        write!(writer, "{}", value)
×
68
    } else if peek.shape().is_type::<u16>() {
8✔
69
        let value = peek.get::<u16>().unwrap();
×
70
        write!(writer, "{}", value)
×
71
    } else if peek.shape().is_type::<u32>() {
8✔
72
        let value = peek.get::<u32>().unwrap();
×
73
        write!(writer, "{}", value)
×
74
    } else if peek.shape().is_type::<u64>() {
8✔
75
        let value = peek.get::<u64>().unwrap();
×
76
        write!(writer, "{}", value)
×
77
    } else if peek.shape().is_type::<usize>() {
8✔
78
        let value = peek.get::<usize>().unwrap();
×
79
        write!(writer, "{}", value)
×
80
    } else if peek.shape().is_type::<i8>() {
8✔
81
        let value = peek.get::<i8>().unwrap();
×
82
        write!(writer, "{}", value)
×
83
    } else if peek.shape().is_type::<i16>() {
8✔
84
        let value = peek.get::<i16>().unwrap();
×
85
        write!(writer, "{}", value)
×
86
    } else if peek.shape().is_type::<i32>() {
8✔
87
        let value = peek.get::<i32>().unwrap();
5✔
88
        write!(writer, "{}", value)
5✔
89
    } else if peek.shape().is_type::<i64>() {
3✔
90
        let value = peek.get::<i64>().unwrap();
×
91
        write!(writer, "{}", value)
×
92
    } else if peek.shape().is_type::<isize>() {
3✔
93
        let value = peek.get::<isize>().unwrap();
×
94
        write!(writer, "{}", value)
×
95
    }
96
    // NonZero types
97
    else if peek.shape().is_type::<NonZero<u8>>() {
3✔
98
        let value = peek.get::<NonZero<u8>>().unwrap();
1✔
99
        write!(writer, "{}", value)
1✔
100
    } else if peek.shape().is_type::<NonZero<u16>>() {
2✔
101
        let value = peek.get::<NonZero<u16>>().unwrap();
×
102
        write!(writer, "{}", value)
×
103
    } else if peek.shape().is_type::<NonZero<u32>>() {
2✔
104
        let value = peek.get::<NonZero<u32>>().unwrap();
×
105
        write!(writer, "{}", value)
×
106
    } else if peek.shape().is_type::<NonZero<u64>>() {
2✔
107
        let value = peek.get::<NonZero<u64>>().unwrap();
×
108
        write!(writer, "{}", value)
×
109
    } else if peek.shape().is_type::<NonZero<usize>>() {
2✔
110
        let value = peek.get::<NonZero<usize>>().unwrap();
×
111
        write!(writer, "{}", value)
×
112
    } else if peek.shape().is_type::<NonZero<i8>>() {
2✔
113
        let value = peek.get::<NonZero<i8>>().unwrap();
×
114
        write!(writer, "{}", value)
×
115
    } else if peek.shape().is_type::<NonZero<i16>>() {
2✔
116
        let value = peek.get::<NonZero<i16>>().unwrap();
×
117
        write!(writer, "{}", value)
×
118
    } else if peek.shape().is_type::<NonZero<i32>>() {
2✔
119
        let value = peek.get::<NonZero<i32>>().unwrap();
×
120
        write!(writer, "{}", value)
×
121
    } else if peek.shape().is_type::<NonZero<i64>>() {
2✔
122
        let value = peek.get::<NonZero<i64>>().unwrap();
×
123
        write!(writer, "{}", value)
×
124
    } else if peek.shape().is_type::<NonZero<isize>>() {
2✔
125
        let value = peek.get::<NonZero<isize>>().unwrap();
×
126
        write!(writer, "{}", value)
×
127
    }
128
    // Float types
129
    else if peek.shape().is_type::<f32>() {
2✔
130
        let value = peek.get::<f32>().unwrap();
2✔
131
        write!(writer, "{}", value)
2✔
132
    } else if peek.shape().is_type::<f64>() {
×
133
        let value = peek.get::<f64>().unwrap();
×
134
        write!(writer, "{}", value)
×
135
    } else {
136
        Err(io::Error::new(
×
137
            io::ErrorKind::Other,
×
138
            format!("Unsupported scalar type: {}", peek.shape()),
×
139
        ))
×
140
    }
141
}
37✔
142

143
/// Serializes a struct to JSON
144
fn serialize_struct<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
24✔
145
    let struct_peek = peek
24✔
146
        .into_struct()
24✔
147
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a struct: {}", e)))?;
24✔
148

149
    write!(writer, "{{")?;
24✔
150

151
    let mut first = true;
24✔
152
    for (field, field_peek) in struct_peek.fields_for_serialize() {
38✔
153
        if !first {
38✔
154
            write!(writer, ",")?;
14✔
155
        }
24✔
156
        first = false;
38✔
157

38✔
158
        // Check for rename attribute
38✔
159
        let field_name = field
38✔
160
            .attributes
38✔
161
            .iter()
38✔
162
            .find_map(|attr| {
38✔
163
                if let FieldAttribute::Rename(name) = attr {
23✔
164
                    Some(*name)
23✔
165
                } else {
166
                    None
×
167
                }
168
            })
23✔
169
            .unwrap_or(field.name);
38✔
170

38✔
171
        // Write field name
38✔
172
        write_json_string(writer, field_name)?;
38✔
173
        write!(writer, ":")?;
38✔
174

175
        // Write field value
176
        serialize(&field_peek, writer)?;
38✔
177
    }
178

179
    write!(writer, "}}")?;
24✔
180

181
    Ok(())
24✔
182
}
24✔
183

184
/// Serializes a list to JSON
185
fn serialize_list<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
2✔
186
    let list_peek = peek
2✔
187
        .into_list()
2✔
188
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a list: {}", e)))?;
2✔
189

190
    write!(writer, "[")?;
2✔
191

192
    let mut first = true;
2✔
193
    for item_peek in list_peek.iter() {
4✔
194
        if !first {
4✔
195
            write!(writer, ",")?;
2✔
196
        }
2✔
197
        first = false;
4✔
198

4✔
199
        serialize(&item_peek, writer)?;
4✔
200
    }
201

202
    write!(writer, "]")?;
2✔
203

204
    Ok(())
2✔
205
}
2✔
206

207
/// Serializes a map to JSON
208
fn serialize_map<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
2✔
209
    let map_peek = peek
2✔
210
        .into_map()
2✔
211
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a map: {}", e)))?;
2✔
212

213
    write!(writer, "{{")?;
2✔
214

215
    let mut first = true;
2✔
216
    for (key, value) in map_peek.iter() {
2✔
217
        if !first {
2✔
218
            write!(writer, ",")?;
×
219
        }
2✔
220
        first = false;
2✔
221

2✔
222
        // For map, keys must be converted to strings
2✔
223
        match key.shape().def {
2✔
224
            Def::Scalar(_) => {
225
                // Try to convert key to string
226
                if key.shape().is_type::<String>() {
2✔
227
                    let key_str = key.get::<String>().unwrap();
×
228
                    write_json_string(writer, key_str)?;
×
229
                } else {
230
                    // For other scalar types, use their Display implementation
231
                    write!(writer, "\"{}\"", key)?;
2✔
232
                }
233
            }
234
            _ => {
235
                return Err(io::Error::new(
×
236
                    io::ErrorKind::Other,
×
237
                    format!("Map keys must be scalar types, got: {}", key.shape()),
×
238
                ));
×
239
            }
240
        }
241

242
        write!(writer, ":")?;
2✔
243

244
        // Write map value
245
        serialize(&value, writer)?;
2✔
246
    }
247

248
    write!(writer, "}}")?;
2✔
249

250
    Ok(())
2✔
251
}
2✔
252

253
/// Serializes an enum to JSON
254
fn serialize_enum<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
1✔
255
    let enum_peek = peek
1✔
256
        .into_enum()
1✔
257
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an enum: {}", e)))?;
1✔
258

259
    let variant = enum_peek.active_variant();
1✔
260
    let variant_name = variant.name;
1✔
261

1✔
262
    // Check if this is a unit variant or a variant with data
1✔
263
    if variant.data.fields.is_empty() {
1✔
264
        // Unit variant - just output the name as a string
265
        write_json_string(writer, variant_name)
×
266
    } else {
267
        // Variant with data - output as an object with a single key
268
        write!(writer, "{{")?;
1✔
269
        write_json_string(writer, variant_name)?;
1✔
270
        write!(writer, ":")?;
1✔
271

272
        // If it's a single-field tuple variant, output just the value
273
        if variant.data.fields.len() == 1 {
1✔
274
            let field = enum_peek.field(0).ok_or_else(|| {
×
275
                io::Error::new(io::ErrorKind::Other, "Failed to access enum field")
×
276
            })?;
×
277
            serialize(&field, writer)?;
×
278
        } else {
279
            // Multi-field variant - output as an array or object depending on variant type
280
            let is_struct = variant
1✔
281
                .data
1✔
282
                .fields
1✔
283
                .iter()
1✔
284
                .any(|f| !f.name.starts_with("__"));
1✔
285

286
            if is_struct {
1✔
287
                // Struct variant - output as an object
288
                write!(writer, "{{")?;
1✔
289

290
                let mut first = true;
1✔
291
                for (field, field_peek) in enum_peek.fields_for_serialize() {
1✔
292
                    if !first {
1✔
293
                        write!(writer, ",")?;
×
294
                    }
1✔
295
                    first = false;
1✔
296

1✔
297
                    write_json_string(writer, field.name)?;
1✔
298
                    write!(writer, ":")?;
1✔
299
                    serialize(&field_peek, writer)?;
1✔
300
                }
301

302
                write!(writer, "}}")?
1✔
303
            } else {
304
                // Tuple variant - output as an array
305
                write!(writer, "[")?;
×
306

307
                let mut first = true;
×
NEW
UNCOV
308
                for (_field, field_peek) in enum_peek.fields_for_serialize() {
×
UNCOV
309
                    if !first {
×
310
                        write!(writer, ",")?;
×
UNCOV
311
                    }
×
UNCOV
312
                    first = false;
×
NEW
313
                    serialize(&field_peek, writer)?;
×
314
                }
315

316
                write!(writer, "]")?;
×
317
            }
318
        }
319

320
        write!(writer, "}}")?;
1✔
321
        Ok(())
1✔
322
    }
323
}
1✔
324

325
/// Serializes an `Option<T>` to JSON
326
fn serialize_option<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
6✔
327
    let option_peek = peek
6✔
328
        .into_option()
6✔
329
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an option: {}", e)))?;
6✔
330

331
    if option_peek.is_none() {
6✔
332
        write!(writer, "null")
3✔
333
    } else {
334
        let value = option_peek
3✔
335
            .value()
3✔
336
            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get option value"))?;
3✔
337
        serialize(&value, writer)
3✔
338
    }
339
}
6✔
340

341
/// Properly escapes and writes a JSON string
342
fn write_json_string<W: Write>(writer: &mut W, s: &str) -> io::Result<()> {
69✔
343
    write!(writer, "\"")?;
69✔
344

345
    for c in s.chars() {
456✔
346
        match c {
456✔
347
            '"' => write!(writer, "\\\"")?,
×
348
            '\\' => write!(writer, "\\\\")?,
×
349
            '\n' => write!(writer, "\\n")?,
×
350
            '\r' => write!(writer, "\\r")?,
×
351
            '\t' => write!(writer, "\\t")?,
×
352
            '\u{08}' => write!(writer, "\\b")?,
×
353
            '\u{0C}' => write!(writer, "\\f")?,
×
354
            c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
456✔
355
            c => write!(writer, "{}", c)?,
456✔
356
        }
357
    }
358

359
    write!(writer, "\"")
69✔
360
}
69✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc