erltf_serde/
de.rs

1// Copyright (C) 2025-2026 Michael S. Klishin and Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error::{Error, Result};
16use erltf::term::OwnedTerm;
17use erltf::types::Atom;
18use serde::de::{DeserializeSeed, EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor};
19use serde::{Deserialize, Deserializer as SerdeDeserializer};
20use std::collections::BTreeMap;
21use std::collections::btree_map;
22use std::sync::OnceLock;
23
24pub fn from_bytes<T: for<'a> Deserialize<'a>>(bytes: &[u8]) -> Result<T> {
25    let term = erltf::decode(bytes).map_err(|e| Error::Erltf(e.into()))?;
26    from_term(&term)
27}
28
29pub fn from_term<'a, T: Deserialize<'a>>(term: &'a OwnedTerm) -> Result<T> {
30    let mut deserializer = Deserializer { term };
31    T::deserialize(&mut deserializer)
32}
33
34pub fn from_proplist<'a, T: Deserialize<'a>>(term: &'a OwnedTerm) -> Result<T> {
35    match term {
36        OwnedTerm::List(elements) => {
37            let deserializer = ProplistDeserializer::new(elements);
38            T::deserialize(deserializer)
39        }
40        OwnedTerm::Nil => {
41            let deserializer = ProplistDeserializer::new(&[]);
42            T::deserialize(deserializer)
43        }
44        _ => Err(Error::TypeMismatch {
45            expected: "proplist (list of tuples)".into(),
46            found: format!("{:?}", term),
47        }),
48    }
49}
50
51pub struct Deserializer<'de> {
52    term: &'de OwnedTerm,
53}
54
55impl<'de> Deserializer<'de> {
56    fn expect_atom(&self, expected: &str) -> Result<&Atom> {
57        match self.term {
58            OwnedTerm::Atom(atom) => {
59                if atom.as_str() == expected {
60                    Ok(atom)
61                } else {
62                    Err(Error::TypeMismatch {
63                        expected: format!("atom '{}'", expected),
64                        found: format!("atom '{}'", atom.as_str()),
65                    })
66                }
67            }
68            _ => Err(Error::TypeMismatch {
69                expected: format!("atom '{}'", expected),
70                found: format!("{:?}", self.term),
71            }),
72        }
73    }
74}
75
76impl<'de> SerdeDeserializer<'de> for &mut Deserializer<'de> {
77    type Error = Error;
78
79    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
80        match self.term {
81            OwnedTerm::Atom(atom) => match atom.as_str() {
82                "true" => visitor.visit_bool(true),
83                "false" => visitor.visit_bool(false),
84                "nil" => visitor.visit_unit(),
85                "undefined" => visitor.visit_none(),
86                _ => visitor.visit_str(atom.as_str()),
87            },
88            OwnedTerm::Integer(i) => visitor.visit_i64(*i),
89            OwnedTerm::Float(f) => visitor.visit_f64(*f),
90            OwnedTerm::Binary(b) => {
91                if let Ok(s) = std::str::from_utf8(b) {
92                    visitor.visit_str(s)
93                } else {
94                    visitor.visit_bytes(b)
95                }
96            }
97            OwnedTerm::String(s) => visitor.visit_str(s),
98            OwnedTerm::List(l) => visitor.visit_seq(SeqDeserializer::new(l)),
99            OwnedTerm::Tuple(t) => visitor.visit_seq(SeqDeserializer::new(t)),
100            OwnedTerm::Map(m) => visitor.visit_map(MapDeserializer::new(m)),
101            OwnedTerm::Nil => visitor.visit_seq(SeqDeserializer::new(&[])),
102            _ => Err(Error::UnsupportedType(format!("{:?}", self.term))),
103        }
104    }
105
106    fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
107        match self.term {
108            OwnedTerm::Atom(atom) => match atom.as_str() {
109                "true" => visitor.visit_bool(true),
110                "false" => visitor.visit_bool(false),
111                _ => Err(Error::TypeMismatch {
112                    expected: "bool atom".into(),
113                    found: format!("{:?}", atom),
114                }),
115            },
116            _ => Err(Error::TypeMismatch {
117                expected: "bool atom".into(),
118                found: format!("{:?}", self.term),
119            }),
120        }
121    }
122
123    fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
124        match self.term {
125            OwnedTerm::Integer(i) => i8::try_from(*i)
126                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for i8", i)))
127                .and_then(|v| visitor.visit_i8(v)),
128            _ => Err(Error::TypeMismatch {
129                expected: "integer".into(),
130                found: format!("{:?}", self.term),
131            }),
132        }
133    }
134
135    fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
136        match self.term {
137            OwnedTerm::Integer(i) => i16::try_from(*i)
138                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for i16", i)))
139                .and_then(|v| visitor.visit_i16(v)),
140            _ => Err(Error::TypeMismatch {
141                expected: "integer".into(),
142                found: format!("{:?}", self.term),
143            }),
144        }
145    }
146
147    fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
148        match self.term {
149            OwnedTerm::Integer(i) => i32::try_from(*i)
150                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for i32", i)))
151                .and_then(|v| visitor.visit_i32(v)),
152            _ => Err(Error::TypeMismatch {
153                expected: "integer".into(),
154                found: format!("{:?}", self.term),
155            }),
156        }
157    }
158
159    fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
160        match self.term {
161            OwnedTerm::Integer(i) => visitor.visit_i64(*i),
162            _ => Err(Error::TypeMismatch {
163                expected: "integer".into(),
164                found: format!("{:?}", self.term),
165            }),
166        }
167    }
168
169    fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
170        match self.term {
171            OwnedTerm::Integer(i) => u8::try_from(*i)
172                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for u8", i)))
173                .and_then(|v| visitor.visit_u8(v)),
174            _ => Err(Error::TypeMismatch {
175                expected: "integer".into(),
176                found: format!("{:?}", self.term),
177            }),
178        }
179    }
180
181    fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
182        match self.term {
183            OwnedTerm::Integer(i) => u16::try_from(*i)
184                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for u16", i)))
185                .and_then(|v| visitor.visit_u16(v)),
186            _ => Err(Error::TypeMismatch {
187                expected: "integer".into(),
188                found: format!("{:?}", self.term),
189            }),
190        }
191    }
192
193    fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
194        match self.term {
195            OwnedTerm::Integer(i) => u32::try_from(*i)
196                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for u32", i)))
197                .and_then(|v| visitor.visit_u32(v)),
198            _ => Err(Error::TypeMismatch {
199                expected: "integer".into(),
200                found: format!("{:?}", self.term),
201            }),
202        }
203    }
204
205    fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
206        match self.term {
207            OwnedTerm::Integer(i) => u64::try_from(*i)
208                .map_err(|_| Error::InvalidValue(format!("integer {} out of range for u64", i)))
209                .and_then(|v| visitor.visit_u64(v)),
210            OwnedTerm::BigInt(big) if big.sign.is_positive() && big.digits.len() <= 8 => {
211                let mut bytes = [0u8; 8];
212                bytes[..big.digits.len()].copy_from_slice(&big.digits);
213                let value = u64::from_le_bytes(bytes);
214                visitor.visit_u64(value)
215            }
216            _ => Err(Error::TypeMismatch {
217                expected: "integer or unsigned bigint".into(),
218                found: format!("{:?}", self.term),
219            }),
220        }
221    }
222
223    fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
224        match self.term {
225            OwnedTerm::Float(f) => visitor.visit_f32(*f as f32),
226            _ => Err(Error::TypeMismatch {
227                expected: "float".into(),
228                found: format!("{:?}", self.term),
229            }),
230        }
231    }
232
233    fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
234        match self.term {
235            OwnedTerm::Float(f) => visitor.visit_f64(*f),
236            _ => Err(Error::TypeMismatch {
237                expected: "float".into(),
238                found: format!("{:?}", self.term),
239            }),
240        }
241    }
242
243    fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
244        match self.term {
245            OwnedTerm::String(s) => {
246                let mut chars = s.chars();
247                if let Some(c) = chars.next()
248                    && chars.next().is_none()
249                {
250                    return visitor.visit_char(c);
251                }
252                Err(Error::InvalidValue("expected single char".into()))
253            }
254            _ => Err(Error::TypeMismatch {
255                expected: "string".into(),
256                found: format!("{:?}", self.term),
257            }),
258        }
259    }
260
261    fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
262        match self.term {
263            OwnedTerm::Binary(b) => {
264                let s = std::str::from_utf8(b).map_err(|e| Error::InvalidValue(e.to_string()))?;
265                visitor.visit_borrowed_str(s)
266            }
267            OwnedTerm::String(s) => visitor.visit_borrowed_str(s),
268            OwnedTerm::Atom(a) => visitor.visit_borrowed_str(a.as_str()),
269            _ => Err(Error::TypeMismatch {
270                expected: "string or binary".into(),
271                found: format!("{:?}", self.term),
272            }),
273        }
274    }
275
276    fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
277        self.deserialize_str(visitor)
278    }
279
280    fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
281        match self.term {
282            OwnedTerm::Binary(b) => visitor.visit_borrowed_bytes(b),
283            _ => Err(Error::TypeMismatch {
284                expected: "binary".into(),
285                found: format!("{:?}", self.term),
286            }),
287        }
288    }
289
290    fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
291        self.deserialize_bytes(visitor)
292    }
293
294    fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
295        match self.term {
296            OwnedTerm::Atom(atom) if atom.as_str() == "undefined" => visitor.visit_none(),
297            _ => visitor.visit_some(self),
298        }
299    }
300
301    fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
302        self.expect_atom("nil")?;
303        visitor.visit_unit()
304    }
305
306    fn deserialize_unit_struct<V: Visitor<'de>>(
307        self,
308        name: &'static str,
309        visitor: V,
310    ) -> Result<V::Value> {
311        self.expect_atom(name)?;
312        visitor.visit_unit()
313    }
314
315    fn deserialize_newtype_struct<V: Visitor<'de>>(
316        self,
317        _name: &'static str,
318        visitor: V,
319    ) -> Result<V::Value> {
320        visitor.visit_newtype_struct(self)
321    }
322
323    fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
324        match self.term {
325            OwnedTerm::List(l) => visitor.visit_seq(SeqDeserializer::new(l)),
326            OwnedTerm::Nil => visitor.visit_seq(SeqDeserializer::new(&[])),
327            _ => Err(Error::TypeMismatch {
328                expected: "list".into(),
329                found: format!("{:?}", self.term),
330            }),
331        }
332    }
333
334    fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, visitor: V) -> Result<V::Value> {
335        match self.term {
336            OwnedTerm::Tuple(t) => visitor.visit_seq(SeqDeserializer::new(t)),
337            _ => Err(Error::TypeMismatch {
338                expected: "tuple".into(),
339                found: format!("{:?}", self.term),
340            }),
341        }
342    }
343
344    fn deserialize_tuple_struct<V: Visitor<'de>>(
345        self,
346        _name: &'static str,
347        _len: usize,
348        visitor: V,
349    ) -> Result<V::Value> {
350        match self.term {
351            OwnedTerm::Tuple(t) => visitor.visit_seq(SeqDeserializer::new(t)),
352            _ => Err(Error::TypeMismatch {
353                expected: "tuple".into(),
354                found: format!("{:?}", self.term),
355            }),
356        }
357    }
358
359    fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
360        match self.term {
361            OwnedTerm::Map(m) => visitor.visit_map(MapDeserializer::new(m)),
362            _ => Err(Error::TypeMismatch {
363                expected: "map".into(),
364                found: format!("{:?}", self.term),
365            }),
366        }
367    }
368
369    fn deserialize_struct<V: Visitor<'de>>(
370        self,
371        _name: &'static str,
372        _fields: &'static [&'static str],
373        visitor: V,
374    ) -> Result<V::Value> {
375        self.deserialize_map(visitor)
376    }
377
378    fn deserialize_enum<V: Visitor<'de>>(
379        self,
380        _name: &'static str,
381        _variants: &'static [&'static str],
382        visitor: V,
383    ) -> Result<V::Value> {
384        match self.term {
385            OwnedTerm::Atom(_) => visitor.visit_enum(EnumDeserializer { term: self.term }),
386            OwnedTerm::Tuple(elements) if !elements.is_empty() => {
387                visitor.visit_enum(EnumDeserializer { term: self.term })
388            }
389            _ => Err(Error::TypeMismatch {
390                expected: "atom or tuple".into(),
391                found: format!("{:?}", self.term),
392            }),
393        }
394    }
395
396    fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
397        self.deserialize_str(visitor)
398    }
399
400    fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
401        visitor.visit_unit()
402    }
403}
404
405struct SeqDeserializer<'de> {
406    iter: std::slice::Iter<'de, OwnedTerm>,
407}
408
409impl<'de> SeqDeserializer<'de> {
410    fn new(slice: &'de [OwnedTerm]) -> Self {
411        SeqDeserializer { iter: slice.iter() }
412    }
413}
414
415impl<'de> SeqAccess<'de> for SeqDeserializer<'de> {
416    type Error = Error;
417
418    fn next_element_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Value>> {
419        match self.iter.next() {
420            Some(term) => {
421                let mut de = Deserializer { term };
422                seed.deserialize(&mut de).map(Some)
423            }
424            None => Ok(None),
425        }
426    }
427}
428
429struct MapDeserializer<'de> {
430    iter: btree_map::Iter<'de, OwnedTerm, OwnedTerm>,
431    value: Option<&'de OwnedTerm>,
432}
433
434impl<'de> MapDeserializer<'de> {
435    fn new(map: &'de BTreeMap<OwnedTerm, OwnedTerm>) -> Self {
436        MapDeserializer {
437            iter: map.iter(),
438            value: None,
439        }
440    }
441}
442
443impl<'de> MapAccess<'de> for MapDeserializer<'de> {
444    type Error = Error;
445
446    fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> {
447        match self.iter.next() {
448            Some((key, value)) => {
449                self.value = Some(value);
450                let mut de = Deserializer { term: key };
451                seed.deserialize(&mut de).map(Some)
452            }
453            None => Ok(None),
454        }
455    }
456
457    fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value> {
458        match self.value.take() {
459            Some(value) => {
460                let mut de = Deserializer { term: value };
461                seed.deserialize(&mut de)
462            }
463            None => Err(Error::Message("next_value called without next_key".into())),
464        }
465    }
466}
467
468struct EnumDeserializer<'de> {
469    term: &'de OwnedTerm,
470}
471
472impl<'de> EnumAccess<'de> for EnumDeserializer<'de> {
473    type Error = Error;
474    type Variant = VariantDeserializer<'de>;
475
476    fn variant_seed<V: DeserializeSeed<'de>>(self, seed: V) -> Result<(V::Value, Self::Variant)> {
477        match self.term {
478            OwnedTerm::Atom(_) => {
479                let mut de = Deserializer { term: self.term };
480                let val = seed.deserialize(&mut de)?;
481                Ok((val, VariantDeserializer { rest: &[] }))
482            }
483            OwnedTerm::Tuple(elements) if !elements.is_empty() => {
484                let mut de = Deserializer { term: &elements[0] };
485                let val = seed.deserialize(&mut de)?;
486                let rest = if elements.len() > 1 {
487                    &elements[1..]
488                } else {
489                    &elements[0..0]
490                };
491                Ok((val, VariantDeserializer { rest }))
492            }
493            _ => Err(Error::TypeMismatch {
494                expected: "enum (atom or tuple)".into(),
495                found: format!("{:?}", self.term),
496            }),
497        }
498    }
499}
500
501struct VariantDeserializer<'de> {
502    rest: &'de [OwnedTerm],
503}
504
505impl<'de> VariantAccess<'de> for VariantDeserializer<'de> {
506    type Error = Error;
507
508    fn unit_variant(self) -> Result<()> {
509        if self.rest.is_empty() {
510            Ok(())
511        } else {
512            Err(Error::TypeMismatch {
513                expected: "unit variant".into(),
514                found: format!("variant with {} elements", self.rest.len()),
515            })
516        }
517    }
518
519    fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value> {
520        if self.rest.len() == 1 {
521            let mut de = Deserializer {
522                term: &self.rest[0],
523            };
524            seed.deserialize(&mut de)
525        } else {
526            Err(Error::TypeMismatch {
527                expected: "newtype variant with 1 element".into(),
528                found: format!("variant with {} elements", self.rest.len()),
529            })
530        }
531    }
532
533    fn tuple_variant<V: Visitor<'de>>(self, _len: usize, visitor: V) -> Result<V::Value> {
534        visitor.visit_seq(SeqDeserializer::new(self.rest))
535    }
536
537    fn struct_variant<V: Visitor<'de>>(
538        self,
539        _fields: &'static [&'static str],
540        visitor: V,
541    ) -> Result<V::Value> {
542        if self.rest.len() == 1 {
543            match &self.rest[0] {
544                OwnedTerm::Map(m) => visitor.visit_map(MapDeserializer::new(m)),
545                _ => Err(Error::TypeMismatch {
546                    expected: "struct variant (map)".into(),
547                    found: format!("{:?}", self.rest[0]),
548                }),
549            }
550        } else {
551            Err(Error::TypeMismatch {
552                expected: "struct variant with 1 map element".into(),
553                found: format!("variant with {} elements", self.rest.len()),
554            })
555        }
556    }
557}
558
559pub struct ProplistDeserializer<'de> {
560    elements: &'de [OwnedTerm],
561}
562
563impl<'de> ProplistDeserializer<'de> {
564    pub fn new(elements: &'de [OwnedTerm]) -> Self {
565        ProplistDeserializer { elements }
566    }
567}
568
569impl<'de> SerdeDeserializer<'de> for ProplistDeserializer<'de> {
570    type Error = Error;
571
572    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
573        visitor.visit_map(ProplistMapAccess {
574            elements: self.elements,
575            index: 0,
576            current_value: ProplistValue::None,
577        })
578    }
579
580    fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
581        visitor.visit_map(ProplistMapAccess {
582            elements: self.elements,
583            index: 0,
584            current_value: ProplistValue::None,
585        })
586    }
587
588    fn deserialize_struct<V: Visitor<'de>>(
589        self,
590        _name: &'static str,
591        _fields: &'static [&'static str],
592        visitor: V,
593    ) -> Result<V::Value> {
594        self.deserialize_map(visitor)
595    }
596
597    serde::forward_to_deserialize_any! {
598        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string
599        bytes byte_buf option unit unit_struct newtype_struct seq tuple
600        tuple_struct enum identifier ignored_any
601    }
602}
603
604struct ProplistMapAccess<'de> {
605    elements: &'de [OwnedTerm],
606    index: usize,
607    current_value: ProplistValue<'de>,
608}
609
610enum ProplistValue<'de> {
611    None,
612    Ref(&'de OwnedTerm),
613    BareAtom,
614}
615
616impl<'de> MapAccess<'de> for ProplistMapAccess<'de> {
617    type Error = Error;
618
619    fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> {
620        while self.index < self.elements.len() {
621            let element = &self.elements[self.index];
622            self.index += 1;
623
624            match element {
625                OwnedTerm::Tuple(t) if t.len() == 2 => {
626                    self.current_value = ProplistValue::Ref(&t[1]);
627                    let mut de = Deserializer { term: &t[0] };
628                    return seed.deserialize(&mut de).map(Some);
629                }
630                OwnedTerm::Atom(_) => {
631                    self.current_value = ProplistValue::BareAtom;
632                    let mut de = Deserializer { term: element };
633                    return seed.deserialize(&mut de).map(Some);
634                }
635                _ => continue,
636            }
637        }
638        Ok(None)
639    }
640
641    fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value> {
642        match std::mem::replace(&mut self.current_value, ProplistValue::None) {
643            ProplistValue::Ref(value) => {
644                let mut de = Deserializer { term: value };
645                seed.deserialize(&mut de)
646            }
647            ProplistValue::BareAtom => {
648                static TRUE_TERM: OnceLock<OwnedTerm> = OnceLock::new();
649                let true_term = TRUE_TERM.get_or_init(|| OwnedTerm::Atom(Atom::new("true")));
650                let mut de = Deserializer { term: true_term };
651                seed.deserialize(&mut de)
652            }
653            ProplistValue::None => Err(Error::Message("next_value called without next_key".into())),
654        }
655    }
656}