Skip to main content

facet_cbor/
serialize.rs

1use facet::Facet;
2use facet_core::{Def, ScalarType, StructKind, Type, UserType};
3use facet_reflect::Peek;
4
5use crate::encode;
6use crate::error::CborError;
7
8/// Serialize any `Facet` type to CBOR bytes.
9pub fn to_vec<'a, T: Facet<'a>>(value: &T) -> Result<Vec<u8>, CborError> {
10    let peek = Peek::new(value);
11    let mut out = Vec::new();
12    serialize_peek(peek, &mut out)?;
13    Ok(out)
14}
15
16/// Serialize a `Peek` value to CBOR, appending to `out`.
17pub fn serialize_peek(peek: Peek<'_, '_>, out: &mut Vec<u8>) -> Result<(), CborError> {
18    // Unwrap transparent wrappers (NonZero, newtypes, smart pointers, etc.)
19    let peek = peek.innermost_peek();
20
21    // Try scalar first
22    if let Some(scalar_type) = peek.scalar_type() {
23        return serialize_scalar(peek, scalar_type, out);
24    }
25
26    // Check def-based types first (Option, List, Map, etc.) before user types,
27    // because Option<T> is both Def::Option and UserType::Enum.
28    match peek.shape().def {
29        Def::Option(_) => {
30            let opt = peek
31                .into_option()
32                .map_err(|e| CborError::ReflectError(e.to_string()))?;
33            return match opt.value() {
34                Some(inner) => serialize_peek(inner, out),
35                None => {
36                    encode::write_null(out);
37                    Ok(())
38                }
39            };
40        }
41        Def::List(list_def) => {
42            // Special case: Vec<u8> → byte string
43            if list_def.t().is_type::<u8>() {
44                let list = peek
45                    .into_list()
46                    .map_err(|e| CborError::ReflectError(e.to_string()))?;
47                let len = list.len();
48                let mut bytes = Vec::with_capacity(len);
49                for i in 0..len {
50                    let elem = list.get(i).ok_or_else(|| {
51                        CborError::ReflectError("list index out of bounds".into())
52                    })?;
53                    let byte = elem
54                        .get::<u8>()
55                        .map_err(|e| CborError::ReflectError(e.to_string()))?;
56                    bytes.push(*byte);
57                }
58                encode::write_bytes(out, &bytes);
59            } else {
60                let list = peek
61                    .into_list()
62                    .map_err(|e| CborError::ReflectError(e.to_string()))?;
63                let len = list.len();
64                encode::write_array_header(out, len as u64);
65                for elem in list.iter() {
66                    serialize_peek(elem, out)?;
67                }
68            }
69            return Ok(());
70        }
71        Def::Array(_) | Def::Slice(_) => {
72            let list_like = peek
73                .into_list_like()
74                .map_err(|e| CborError::ReflectError(e.to_string()))?;
75            let len = list_like.len();
76            encode::write_array_header(out, len as u64);
77            for elem in list_like.iter() {
78                serialize_peek(elem, out)?;
79            }
80            return Ok(());
81        }
82        Def::Map(_) => {
83            let map = peek
84                .into_map()
85                .map_err(|e| CborError::ReflectError(e.to_string()))?;
86            encode::write_map_header(out, map.len() as u64);
87            for (key, value) in map.iter() {
88                serialize_peek(key, out)?;
89                serialize_peek(value, out)?;
90            }
91            return Ok(());
92        }
93        Def::Set(_) => {
94            let set = peek
95                .into_set()
96                .map_err(|e| CborError::ReflectError(e.to_string()))?;
97            encode::write_array_header(out, set.len() as u64);
98            for elem in set.iter() {
99                serialize_peek(elem, out)?;
100            }
101            return Ok(());
102        }
103        Def::Pointer(_) => {
104            let ptr = peek
105                .into_pointer()
106                .map_err(|e| CborError::ReflectError(e.to_string()))?;
107            return match ptr.borrow_inner() {
108                Some(inner) => serialize_peek(inner, out),
109                None => {
110                    encode::write_null(out);
111                    Ok(())
112                }
113            };
114        }
115        _ => {}
116    }
117
118    // Try struct/enum (user types)
119    match peek.shape().ty {
120        Type::User(UserType::Struct(struct_type)) => match struct_type.kind {
121            StructKind::Struct => {
122                let ps = peek
123                    .into_struct()
124                    .map_err(|e| CborError::ReflectError(e.to_string()))?;
125                encode::write_map_header(out, ps.field_count() as u64);
126                for i in 0..ps.field_count() {
127                    let field = &struct_type.fields[i];
128                    encode::write_text(out, field.name);
129                    let field_peek = ps
130                        .field(i)
131                        .map_err(|e| CborError::ReflectError(e.to_string()))?;
132                    serialize_peek(field_peek, out)?;
133                }
134                return Ok(());
135            }
136            StructKind::TupleStruct | StructKind::Tuple => {
137                let ps = peek
138                    .into_struct()
139                    .map_err(|e| CborError::ReflectError(e.to_string()))?;
140                encode::write_array_header(out, ps.field_count() as u64);
141                for i in 0..ps.field_count() {
142                    let field_peek = ps
143                        .field(i)
144                        .map_err(|e| CborError::ReflectError(e.to_string()))?;
145                    serialize_peek(field_peek, out)?;
146                }
147                return Ok(());
148            }
149            StructKind::Unit => {
150                encode::write_null(out);
151                return Ok(());
152            }
153        },
154        Type::User(UserType::Enum(_)) => {
155            let pe = peek
156                .into_enum()
157                .map_err(|e| CborError::ReflectError(e.to_string()))?;
158            let variant = pe
159                .active_variant()
160                .map_err(|e| CborError::ReflectError(e.to_string()))?;
161
162            let effective_name = variant.rename.unwrap_or(variant.name);
163
164            if let Some(tag_key) = peek.shape().tag {
165                // Internally-tagged enum: tag is merged into the payload
166                return serialize_enum_internally_tagged(pe, variant, effective_name, tag_key, out);
167            }
168
169            // Externally-tagged enum: map with one entry: variant_name → payload
170            encode::write_map_header(out, 1);
171            encode::write_text(out, effective_name);
172
173            match variant.data.kind {
174                StructKind::Unit => {
175                    encode::write_null(out);
176                }
177                StructKind::TupleStruct => {
178                    // Newtype variant if exactly one field, otherwise tuple
179                    if variant.data.fields.len() == 1 {
180                        let field_peek = pe
181                            .field(0)
182                            .map_err(|e| CborError::ReflectError(e.to_string()))?
183                            .ok_or_else(|| {
184                                CborError::ReflectError("missing newtype variant field".into())
185                            })?;
186                        serialize_peek(field_peek, out)?;
187                    } else {
188                        encode::write_array_header(out, variant.data.fields.len() as u64);
189                        for i in 0..variant.data.fields.len() {
190                            let field_peek = pe
191                                .field(i)
192                                .map_err(|e| CborError::ReflectError(e.to_string()))?
193                                .ok_or_else(|| {
194                                    CborError::ReflectError("missing tuple variant field".into())
195                                })?;
196                            serialize_peek(field_peek, out)?;
197                        }
198                    }
199                }
200                StructKind::Tuple => {
201                    encode::write_array_header(out, variant.data.fields.len() as u64);
202                    for i in 0..variant.data.fields.len() {
203                        let field_peek = pe
204                            .field(i)
205                            .map_err(|e| CborError::ReflectError(e.to_string()))?
206                            .ok_or_else(|| {
207                                CborError::ReflectError("missing tuple variant field".into())
208                            })?;
209                        serialize_peek(field_peek, out)?;
210                    }
211                }
212                StructKind::Struct => {
213                    encode::write_map_header(out, variant.data.fields.len() as u64);
214                    for i in 0..variant.data.fields.len() {
215                        let field = &variant.data.fields[i];
216                        encode::write_text(out, field.name);
217                        let field_peek = pe
218                            .field(i)
219                            .map_err(|e| CborError::ReflectError(e.to_string()))?
220                            .ok_or_else(|| {
221                                CborError::ReflectError("missing struct variant field".into())
222                            })?;
223                        serialize_peek(field_peek, out)?;
224                    }
225                }
226            }
227            return Ok(());
228        }
229        _ => {}
230    }
231
232    Err(CborError::UnsupportedType(format!("{}", peek.shape())))
233}
234
235fn serialize_scalar(
236    peek: Peek<'_, '_>,
237    scalar_type: ScalarType,
238    out: &mut Vec<u8>,
239) -> Result<(), CborError> {
240    match scalar_type {
241        ScalarType::Unit => {
242            encode::write_null(out);
243        }
244        ScalarType::Bool => {
245            let v = peek
246                .get::<bool>()
247                .map_err(|e| CborError::ReflectError(e.to_string()))?;
248            encode::write_bool(out, *v);
249        }
250        ScalarType::Char => {
251            let v = peek
252                .get::<char>()
253                .map_err(|e| CborError::ReflectError(e.to_string()))?;
254            let mut buf = [0u8; 4];
255            let s = v.encode_utf8(&mut buf);
256            encode::write_text(out, s);
257        }
258        ScalarType::U8 => {
259            let v = peek
260                .get::<u8>()
261                .map_err(|e| CborError::ReflectError(e.to_string()))?;
262            encode::write_uint(out, *v as u64);
263        }
264        ScalarType::U16 => {
265            let v = peek
266                .get::<u16>()
267                .map_err(|e| CborError::ReflectError(e.to_string()))?;
268            encode::write_uint(out, *v as u64);
269        }
270        ScalarType::U32 => {
271            let v = peek
272                .get::<u32>()
273                .map_err(|e| CborError::ReflectError(e.to_string()))?;
274            encode::write_uint(out, *v as u64);
275        }
276        ScalarType::U64 => {
277            let v = peek
278                .get::<u64>()
279                .map_err(|e| CborError::ReflectError(e.to_string()))?;
280            encode::write_uint(out, *v);
281        }
282        ScalarType::U128 => {
283            let v = peek
284                .get::<u128>()
285                .map_err(|e| CborError::ReflectError(e.to_string()))?;
286            // CBOR only supports up to u64; encode as byte string for u128
287            if *v <= u64::MAX as u128 {
288                encode::write_uint(out, *v as u64);
289            } else {
290                // Tag 2 (positive bignum) + 16-byte big-endian
291                out.push(0xc2);
292                let bytes = v.to_be_bytes();
293                // Strip leading zeros
294                let start = bytes.iter().position(|&b| b != 0).unwrap_or(15);
295                encode::write_bytes(out, &bytes[start..]);
296            }
297        }
298        ScalarType::USize => {
299            let v = peek
300                .get::<usize>()
301                .map_err(|e| CborError::ReflectError(e.to_string()))?;
302            encode::write_uint(out, *v as u64);
303        }
304        ScalarType::I8 => {
305            let v = peek
306                .get::<i8>()
307                .map_err(|e| CborError::ReflectError(e.to_string()))?;
308            write_signed(out, *v as i64);
309        }
310        ScalarType::I16 => {
311            let v = peek
312                .get::<i16>()
313                .map_err(|e| CborError::ReflectError(e.to_string()))?;
314            write_signed(out, *v as i64);
315        }
316        ScalarType::I32 => {
317            let v = peek
318                .get::<i32>()
319                .map_err(|e| CborError::ReflectError(e.to_string()))?;
320            write_signed(out, *v as i64);
321        }
322        ScalarType::I64 => {
323            let v = peek
324                .get::<i64>()
325                .map_err(|e| CborError::ReflectError(e.to_string()))?;
326            write_signed(out, *v);
327        }
328        ScalarType::I128 => {
329            let v = peek
330                .get::<i128>()
331                .map_err(|e| CborError::ReflectError(e.to_string()))?;
332            if *v >= 0 {
333                if *v <= u64::MAX as i128 {
334                    encode::write_uint(out, *v as u64);
335                } else {
336                    // Tag 2 (positive bignum)
337                    out.push(0xc2);
338                    let bytes = (*v as u128).to_be_bytes();
339                    let start = bytes.iter().position(|&b| b != 0).unwrap_or(15);
340                    encode::write_bytes(out, &bytes[start..]);
341                }
342            } else {
343                // For negative: CBOR major 1 encodes -1-n, so n = -1 - v
344                let abs_minus_one = (-1i128 - *v) as u128;
345                if abs_minus_one <= u64::MAX as u128 {
346                    encode::write_neg(out, abs_minus_one as u64);
347                } else {
348                    // Tag 3 (negative bignum)
349                    out.push(0xc3);
350                    let bytes = abs_minus_one.to_be_bytes();
351                    let start = bytes.iter().position(|&b| b != 0).unwrap_or(15);
352                    encode::write_bytes(out, &bytes[start..]);
353                }
354            }
355        }
356        ScalarType::ISize => {
357            let v = peek
358                .get::<isize>()
359                .map_err(|e| CborError::ReflectError(e.to_string()))?;
360            write_signed(out, *v as i64);
361        }
362        ScalarType::F32 => {
363            let v = peek
364                .get::<f32>()
365                .map_err(|e| CborError::ReflectError(e.to_string()))?;
366            encode::write_f32(out, *v);
367        }
368        ScalarType::F64 => {
369            let v = peek
370                .get::<f64>()
371                .map_err(|e| CborError::ReflectError(e.to_string()))?;
372            encode::write_f64(out, *v);
373        }
374        ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
375            let s = peek
376                .as_str()
377                .ok_or_else(|| CborError::ReflectError("failed to extract string value".into()))?;
378            encode::write_text(out, s);
379        }
380        _ => {
381            return Err(CborError::UnsupportedType(format!(
382                "scalar type {scalar_type:?}"
383            )));
384        }
385    }
386    Ok(())
387}
388
389/// Serialize an internally-tagged enum.
390///
391/// When `#[facet(tag = "tag")]` is set on an enum:
392/// - Unit variants → just the tag value as a CBOR text string: `"variant_name"`
393/// - Struct variants → CBOR map with tag field merged: `{ "tag": "variant_name", field1: ..., ... }`
394/// - Newtype/tuple variants → not supported (returns error)
395fn serialize_enum_internally_tagged(
396    pe: facet_reflect::PeekEnum<'_, '_>,
397    variant: &facet_core::Variant,
398    effective_name: &str,
399    tag_key: &str,
400    out: &mut Vec<u8>,
401) -> Result<(), CborError> {
402    match variant.data.kind {
403        StructKind::Unit => {
404            // Unit variant: just the tag value as a string
405            encode::write_text(out, effective_name);
406        }
407        StructKind::Struct => {
408            // Struct variant: map with tag field + all struct fields
409            let field_count = variant.data.fields.len();
410            encode::write_map_header(out, (field_count + 1) as u64);
411            // Write tag entry first
412            encode::write_text(out, tag_key);
413            encode::write_text(out, effective_name);
414            // Write struct fields
415            for i in 0..field_count {
416                let field = &variant.data.fields[i];
417                encode::write_text(out, field.name);
418                let field_peek = pe
419                    .field(i)
420                    .map_err(|e| CborError::ReflectError(e.to_string()))?
421                    .ok_or_else(|| {
422                        CborError::ReflectError("missing struct variant field".into())
423                    })?;
424                serialize_peek(field_peek, out)?;
425            }
426        }
427        StructKind::TupleStruct | StructKind::Tuple => {
428            return Err(CborError::UnsupportedType(format!(
429                "internally-tagged enum cannot have tuple variant '{}'",
430                variant.name
431            )));
432        }
433    }
434    Ok(())
435}
436
437fn write_signed(out: &mut Vec<u8>, v: i64) {
438    if v >= 0 {
439        encode::write_uint(out, v as u64);
440    } else {
441        // CBOR major 1: encode -1-n, so for value v, n = -1 - v
442        encode::write_neg(out, (-1i64 - v) as u64);
443    }
444}