erlang_term/
term.rs

1use crate::raw_term::{RawTermGeneralType, RawTermType};
2use crate::RawTerm;
3
4use keylist::Keylist;
5use nom::error::Error;
6use nom::Err as NomErr;
7use num_bigint::BigInt;
8use ordered_float::OrderedFloat;
9use std::collections::HashMap;
10use std::hash::{Hash, Hasher};
11use std::iter::FromIterator;
12
13mod improper_list;
14mod ord;
15
16pub use improper_list::ImproperList;
17
18#[derive(Debug, Clone, PartialEq)]
19#[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "serde_impl", serde(untagged))]
21/// Higher level Elixir types
22///
23/// These can be created by using the `Term::from` methods or creating them directly.
24///
25/// ```rust
26/// # use erlang_term::Term;
27///
28/// let string = Term::from("ok");
29/// let atom = Term::Atom("ok".to_string());
30///
31/// assert!(string.is_string());
32/// assert!(atom.is_atom());
33/// assert_ne!(string, atom);
34/// ```
35///
36/// The `Term::as_*` will unwrap the Term into a specific type if the Term is that type
37///
38/// ```rust
39/// # use erlang_term::Term;
40///
41/// let atom = Term::Atom("ok".to_string());
42/// assert_eq!(Some("ok".to_string()), atom.as_atom());
43///
44/// let atom = Term::Atom("ok".to_string());
45/// assert_eq!(None, atom.as_string());
46/// ```
47///
48/// Convert to RawTerm
49///
50/// ```rust
51/// # use erlang_term::{Term, RawTerm};
52///
53/// let term = Term::from("testing");
54/// let raw_term = RawTerm::from(term);
55///
56/// assert_eq!(RawTerm::Binary(vec![116, 101, 115, 116, 105, 110, 103]), raw_term);
57/// ```
58///
59/// Convert from RawTerm
60///
61/// ```rust
62/// # use erlang_term::{Term, RawTerm};
63///
64/// let raw_term = RawTerm::Binary(vec![116, 101, 115, 116, 105, 110, 103]);
65/// let term = Term::from(raw_term);
66///
67/// assert_eq!(Some("testing".to_string()), term.as_string());
68/// ```
69///
70pub enum Term {
71    Byte(u8),
72    Int(i32),
73    Float(OrderedFloat<f64>),
74    String(String),
75    Atom(String),
76    Bytes(Vec<u8>),
77    Bool(bool),
78    Nil,
79    BigInt(BigInt),
80    Charlist(Vec<u8>),
81    Map(HashMap<Term, Term>),
82    Keyword(Keylist<String, Term>),
83    List(Vec<Term>),
84    Tuple(Vec<Term>),
85    Other(RawTerm),
86}
87
88// elixir formats lists with numbers below 32 as lists otherwise as charlists
89// elixir formats binaries with numbers below 32 as lists otherwise as string
90
91impl From<RawTerm> for Term {
92    fn from(term: RawTerm) -> Self {
93        use RawTerm::*;
94        use Term::*;
95        match term {
96            SmallInt(x) => Byte(x),
97            RawTerm::Int(x) => Term::Int(x),
98            RawTerm::Float(x) => Term::Float(x),
99            Binary(x) if is_string_printable(&x) => match std::str::from_utf8(&x) {
100                Ok(s) => Term::String(s.to_string()),
101                Err(_) => Bytes(x),
102            },
103            Binary(x) => Bytes(x),
104            RawTerm::String(x) => Charlist(x),
105            SmallBigInt(x) => BigInt(x),
106            LargeBigInt(x) => BigInt(x),
107            RawTerm::List(x) => {
108                if x.iter().all(|x| x.is_atom_pair()) {
109                    let map = Keylist::from_iter(
110                        x.into_iter()
111                            .map(|x| x.as_atom_pair().unwrap())
112                            .map(|(a, b)| (a, Term::from(b))),
113                    );
114                    Term::Keyword(map)
115                } else {
116                    Term::List(raw_term_list_to_term_list(x))
117                }
118            }
119            RawTerm::Nil => Term::List(Vec::new()),
120            SmallTuple(x) => Tuple(raw_term_list_to_term_list(x)),
121            LargeTuple(x) => Tuple(raw_term_list_to_term_list(x)),
122            AtomDeprecated(x) => atom_to_term(x),
123            SmallAtomDeprecated(x) => atom_to_term(x),
124            SmallAtom(x) => atom_to_term(x),
125            RawTerm::Atom(x) => atom_to_term(x),
126            RawTerm::Map(x) => Term::Map(
127                x.into_iter()
128                    .map(|(a, b)| (Term::from(a), Term::from(b)))
129                    .collect(),
130            ),
131            x => Other(x),
132        }
133    }
134}
135
136impl Hash for Term {
137    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
138        use Term::*;
139
140        match self {
141            Byte(x) => x.hash(state),
142            Int(x) => x.hash(state),
143            Float(x) => x.hash(state),
144            String(x) => x.hash(state),
145            Atom(x) => x.hash(state),
146            Bytes(x) => x.hash(state),
147            Bool(x) => x.hash(state),
148            Nil => "nil".hash(state),
149            BigInt(x) => x.hash(state),
150            Charlist(x) => x.hash(state),
151            Map(x) => {
152                // copied from https://github.com/rust-lang/rust/pull/48366
153                state.write_u64(
154                    x.iter()
155                        .map(|kv| {
156                            let mut h = std::collections::hash_map::DefaultHasher::new();
157                            kv.hash(&mut h);
158                            h.finish()
159                        })
160                        .fold(0, u64::wrapping_add),
161                )
162            }
163            Keyword(x) => x.hash(state),
164            List(x) => x.hash(state),
165            Tuple(x) => x.hash(state),
166            Other(x) => x.hash(state),
167        }
168    }
169}
170
171fn is_string_printable(binary: &[u8]) -> bool {
172    binary.iter().all(is_string_printable_byte)
173}
174
175fn is_string_printable_byte(byte: &u8) -> bool {
176    // elixir 0xA0..0xD7FF
177    // let the String::from_utf8 check if it is a valid utf8 string
178    if byte >= &0xA0 {
179        return true;
180    }
181
182    // '\n\r\t\v\b\f\e\d\a'
183    if [10, 13, 9, 11, 8, 12, 27, 127, 7].contains(byte) {
184        return true;
185    }
186    // elixir 0x20..0x7E
187    if (0x20..=0x7E).contains(byte) {
188        return true;
189    }
190
191    false
192}
193
194fn raw_term_list_to_term_list(raw_list: Vec<RawTerm>) -> Vec<Term> {
195    raw_list.into_iter().map(Term::from).collect()
196}
197
198fn atom_to_term(atom: String) -> Term {
199    match atom.as_ref() {
200        "false" => Term::Bool(false),
201        "true" => Term::Bool(true),
202        "nil" => Term::Nil,
203        _ => Term::Atom(atom),
204    }
205}
206
207impl std::fmt::Display for Term {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        write!(f, "{}", print_elixir_term(self))
210    }
211}
212
213pub fn print_elixir_term(term: &Term) -> String {
214    use Term::*;
215
216    match term {
217        Bool(b) => b.to_string(),
218        Nil => "nil".to_string(),
219        String(s) => format!("{:?}", s),
220        Atom(a) => format_atom(a),
221        Byte(b) => b.to_string(),
222        Bytes(b) => {
223            let bytes: Vec<_> = b.iter().map(|x| x.to_string()).collect();
224            let mut inner = bytes.join(", ");
225            inner.insert_str(0, "<<");
226            inner.push_str(">>");
227            inner
228        }
229        Charlist(c) => {
230            format!("{:?}", c)
231        }
232        Int(i) => i.to_string(),
233        Float(f) => f.to_string(),
234        BigInt(b) => b.to_string(),
235        Keyword(k) => {
236            let list: Vec<_> = k
237                .iter()
238                .map(|(k, v)| {
239                    let mut a = format_atom(k);
240                    a = a.trim_start_matches(':').to_string();
241                    format!("{}: {}", a, print_elixir_term(v))
242                })
243                .collect();
244            let mut inner = list.join(", ");
245            inner.insert(0, '[');
246            inner.push(']');
247            inner
248        }
249        List(l) => {
250            let list: Vec<_> = l.iter().map(print_elixir_term).collect();
251            let mut inner = list.join(", ");
252            inner.insert(0, '[');
253            inner.push(']');
254            inner
255        }
256        Tuple(t) => {
257            let list: Vec<_> = t.iter().map(print_elixir_term).collect();
258            let mut inner = list.join(", ");
259            inner.insert(0, '{');
260            inner.push('}');
261            inner
262        }
263        Map(m) => {
264            let list: Vec<_> = m
265                .iter()
266                .map(|(k, v)| format!("{} => {}", print_elixir_term(k), print_elixir_term(v)))
267                .collect();
268            let mut inner = list.join(", ");
269            inner.insert_str(0, "%{");
270            inner.push('}');
271            inner
272        }
273        other => format!("#{:?}", other),
274    }
275}
276
277fn format_atom(a: &str) -> String {
278    if a.is_empty() {
279        return String::from(r#":"""#);
280    }
281    if a.chars().all(|x| x.is_ascii_alphanumeric()) {
282        if a.chars().next().unwrap().is_ascii_uppercase() {
283            return a.to_string();
284        }
285        if !a.chars().next().unwrap().is_ascii_digit() {
286            return format!(":{}", a);
287        }
288    }
289    format!(r#":"{}""#, a)
290}
291
292impl Term {
293    pub fn from_bytes(input: &[u8]) -> Result<Term, NomErr<Error<&[u8]>>> {
294        Ok(Term::from(RawTerm::from_bytes(input)?))
295    }
296
297    pub fn to_bytes(self) -> Vec<u8> {
298        RawTerm::from(self).to_bytes()
299    }
300
301    #[cfg(feature = "zlib")]
302    pub fn to_gzip_bytes(self, level: flate2::Compression) -> std::io::Result<Vec<u8>> {
303        RawTerm::from(self).to_gzip_bytes(level)
304    }
305
306    pub fn as_type(&self) -> RawTermType {
307        match self {
308            Term::Byte(_) => RawTermType::SmallInt,
309            Term::Int(_) => RawTermType::Int,
310            Term::Float(_) => RawTermType::Float,
311            Term::String(_) => RawTermType::Binary,
312            Term::Atom(_) => RawTermType::Atom,
313            Term::Bytes(_) => RawTermType::Binary,
314            Term::Bool(_) => RawTermType::SmallAtom,
315            Term::Nil => RawTermType::SmallAtom,
316            Term::BigInt(_) => RawTermType::LargeBigInt,
317            Term::Charlist(_) => RawTermType::String,
318            Term::Map(_) => RawTermType::Map,
319            Term::Keyword(_) => RawTermType::List,
320            Term::List(_) => RawTermType::List,
321            Term::Tuple(_) => RawTermType::LargeTuple,
322            Term::Other(x) => x.as_type(),
323        }
324    }
325
326    pub fn as_general_type(&self) -> RawTermGeneralType {
327        RawTermGeneralType::from(self.as_type())
328    }
329
330    pub fn is_byte(&self) -> bool {
331        use Term::*;
332        matches!(self, Byte(_))
333    }
334
335    pub fn is_string(&self) -> bool {
336        use Term::*;
337        matches!(self, String(_))
338    }
339
340    pub fn is_atom(&self) -> bool {
341        use Term::*;
342        matches!(self, Atom(_))
343    }
344
345    pub fn is_tuple(&self) -> bool {
346        use Term::*;
347        matches!(self, Tuple(_))
348    }
349
350    ///
351    /// Check if the Term is a tuple of length 2
352    ///
353    /// ```rust
354    /// # use erlang_term::Term;
355    /// let term = Term::from((1, 2));
356    /// assert!(term.is_pair_tuple());
357    /// ```
358    pub fn is_pair_tuple(&self) -> bool {
359        use Term::*;
360        matches!(self, Tuple(x) if x.len() == 2)
361    }
362
363    pub fn is_list(&self) -> bool {
364        use Term::*;
365        matches!(self, List(_))
366    }
367
368    ///
369    /// Check if the Term is of the form `("string", any)`
370    ///
371    /// ```rust
372    /// # use erlang_term::Term;
373    /// let term = Term::from(("test", 1));
374    /// assert!(term.is_string_tuple_pair());
375    /// let term = Term::from((1, 2));
376    /// assert!(!term.is_string_tuple_pair());
377    /// ```
378    pub fn is_string_tuple_pair(&self) -> bool {
379        use Term::*;
380        matches!(self, Tuple(x) if (x.len() == 2) & x[0].is_string())
381    }
382
383    pub fn as_bool(self) -> Option<bool> {
384        use Term::*;
385        match self {
386            Bool(x) => Some(x),
387            _ => None,
388        }
389    }
390
391    pub fn as_nil(self) -> Option<()> {
392        use Term::*;
393        match self {
394            Nil => Some(()),
395            _ => None,
396        }
397    }
398
399    pub fn as_byte(self) -> Option<u8> {
400        use Term::*;
401        match self {
402            Byte(x) => Some(x),
403            _ => None,
404        }
405    }
406    pub fn as_int(self) -> Option<i32> {
407        use Term::*;
408        match self {
409            Int(x) => Some(x),
410            _ => None,
411        }
412    }
413    pub fn as_float(self) -> Option<f64> {
414        use Term::*;
415        match self {
416            Float(x) => Some(*x),
417            _ => None,
418        }
419    }
420    pub fn as_atom(self) -> Option<String> {
421        use Term::*;
422        match self {
423            Atom(x) => Some(x),
424            _ => None,
425        }
426    }
427    pub fn as_string(self) -> Option<String> {
428        use Term::*;
429        match self {
430            String(x) => Some(x),
431            _ => None,
432        }
433    }
434    pub fn as_bytes(self) -> Option<Vec<u8>> {
435        use Term::*;
436        match self {
437            Bytes(x) => Some(x),
438            _ => None,
439        }
440    }
441
442    pub fn as_charlist(self) -> Option<Vec<u8>> {
443        use Term::*;
444        match self {
445            Charlist(x) => Some(x),
446            _ => None,
447        }
448    }
449
450    pub fn as_big_int(self) -> Option<BigInt> {
451        use Term::*;
452        match self {
453            BigInt(x) => Some(x),
454            _ => None,
455        }
456    }
457
458    pub fn as_keyword(self) -> Option<Keylist<String, Term>> {
459        use Term::*;
460        match self {
461            Keyword(x) => Some(x),
462            _ => None,
463        }
464    }
465
466    pub fn as_list(self) -> Option<Vec<Term>> {
467        use Term::*;
468        match self {
469            List(x) => Some(x),
470            _ => None,
471        }
472    }
473
474    pub fn as_tuple(self) -> Option<Vec<Term>> {
475        use Term::*;
476        match self {
477            Tuple(x) => Some(x),
478            _ => None,
479        }
480    }
481
482    pub fn as_map(self) -> Option<HashMap<Term, Term>> {
483        use Term::*;
484        match self {
485            Map(x) => Some(x),
486            _ => None,
487        }
488    }
489
490    ///
491    /// Unwrap and convert the term into a `HashMap<String, Term>` if the term is a map and all the keys are strings
492    /// Note that this will create a new map, so for one-offs you should use the `Term::as_map` function
493    ///
494    pub fn as_string_map(self) -> Option<HashMap<String, Term>> {
495        use Term::*;
496        match self {
497            Map(x) if x.keys().all(|y| y.is_string()) => {
498                let new_map = HashMap::from_iter(
499                    x.into_iter()
500                        .map(|(k, v)| (k.as_string().expect("checked this in the match"), v)),
501                );
502                Some(new_map)
503            }
504            _ => None,
505        }
506    }
507
508    ///
509    /// Unwrap and convert the term into a `HashMap<String, Term>` if the term is a map and all the keys are atoms
510    /// Note that this will create a new map, so for one-offs you should use the `Term::as_map` function
511    ///
512    pub fn as_atom_map(self) -> Option<HashMap<String, Term>> {
513        use Term::*;
514        match self {
515            Map(x) if x.keys().all(|y| y.is_atom()) => {
516                let new_map = HashMap::from_iter(
517                    x.into_iter()
518                        .map(|(k, v)| (k.as_atom().expect("checked this in the match"), v)),
519                );
520                Some(new_map)
521            }
522            _ => None,
523        }
524    }
525}
526
527macro_rules! impl_from_float {
528    ($type: ty) => {
529        impl From<$type> for Term {
530            fn from(input: $type) -> Term {
531                Term::Float((input as f64).into())
532            }
533        }
534    };
535}
536
537macro_rules! impl_from_integer {
538    ($type: ty) => {
539        impl From<$type> for Term {
540            fn from(input: $type) -> Term {
541                Term::Int(input as i32)
542            }
543        }
544    };
545}
546
547macro_rules! impl_from_big_integer {
548    ($type: ty) => {
549        impl From<$type> for Term {
550            fn from(input: $type) -> Term {
551                Term::BigInt(BigInt::from(input))
552            }
553        }
554    };
555}
556
557impl From<bool> for Term {
558    fn from(input: bool) -> Term {
559        Term::Bool(input)
560    }
561}
562
563impl From<u8> for Term {
564    fn from(input: u8) -> Term {
565        Term::Byte(input)
566    }
567}
568
569impl From<()> for Term {
570    fn from(_input: ()) -> Term {
571        Term::Nil
572    }
573}
574
575impl From<String> for Term {
576    fn from(input: String) -> Term {
577        Term::String(input)
578    }
579}
580
581impl From<&str> for Term {
582    fn from(input: &str) -> Term {
583        Term::String(String::from(input))
584    }
585}
586
587macro_rules! impl_tuple {
588    ($($idx:tt $t:tt),+) => {
589        impl<$($t,)+> From<($($t,)+)> for Term
590        where
591            $($t: Into<Term>,)+
592        {
593            fn from(input: ($($t,)+)) -> Self {
594                Term::Tuple(vec!($(
595                   input.$idx.into(),
596                )+))
597            }
598        }
599    };
600}
601
602impl_tuple!(0 A);
603impl_tuple!(0 A, 1 B);
604impl_tuple!(0 A, 1 B, 2 C);
605impl_tuple!(0 A, 1 B, 2 C, 3 D);
606impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
607impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
608impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
609impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
610impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I);
611impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J);
612impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K);
613impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L);
614impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M);
615impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N);
616impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O);
617impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P);
618
619impl<T: Into<Term>> From<Vec<T>> for Term {
620    fn from(input: Vec<T>) -> Term {
621        let data: Vec<Term> = input.into_iter().map(|x| x.into()).collect();
622        if data.iter().all(|x| x.is_byte()) {
623            Term::Bytes(data.into_iter().map(|x| x.as_byte().unwrap()).collect())
624        } else if data.iter().all(|x| x.is_string_tuple_pair()) {
625            Term::Keyword(Keylist::from_iter(
626                data.into_iter()
627                    .map(|x| x.as_tuple().unwrap())
628                    .map(|mut x| {
629                        let second = x.pop().unwrap();
630                        let first = x.pop().unwrap();
631                        (first, second)
632                    })
633                    .map(|(a, b)| (a.as_string().unwrap(), b)),
634            ))
635        } else if data.iter().all(|x| x.is_pair_tuple()) {
636            Term::Map(HashMap::from_iter(
637                data.into_iter()
638                    .map(|x| x.as_tuple().unwrap())
639                    .map(|mut x| {
640                        let second = x.pop().unwrap();
641                        let first = x.pop().unwrap();
642                        (first, second)
643                    }),
644            ))
645        } else {
646            Term::List(data)
647        }
648    }
649}
650
651impl<K: Into<Term>, V: Into<Term>> From<HashMap<K, V>> for Term {
652    fn from(input: HashMap<K, V>) -> Term {
653        Term::Map(HashMap::from_iter(
654            input.into_iter().map(|(k, v)| (k.into(), v.into())),
655        ))
656    }
657}
658
659impl_from_float!(f32);
660impl_from_float!(f64);
661impl_from_integer!(i8);
662impl_from_integer!(i16);
663impl_from_integer!(u16);
664impl_from_integer!(i32);
665impl_from_big_integer!(usize);
666impl_from_big_integer!(u32);
667impl_from_big_integer!(i64);
668impl_from_big_integer!(u64);
669impl_from_big_integer!(i128);
670impl_from_big_integer!(u128);
671
672#[test]
673fn is_string_printable_test() {
674    let data = &[7, 33, 125];
675    assert!(is_string_printable(data));
676    assert!(std::str::from_utf8(data).is_ok());
677
678    let data = &[194, 160];
679    assert!(is_string_printable(data));
680    assert!(std::str::from_utf8(data).is_ok());
681
682    // utf8 but still invalid according this function
683    let data = &[1, 2, 3, 4];
684    assert!(!is_string_printable(data));
685    assert!(std::str::from_utf8(data).is_ok());
686
687    // not utf8 but still valid according this function
688    let data = &[202, 218, 82, 75, 227, 11, 203, 41, 103, 208, 244, 215];
689    assert!(is_string_printable(data));
690    assert!(std::str::from_utf8(data).is_err())
691}
692
693#[cfg(test)]
694mod convert_tests {
695    use crate::{from_bytes, read_binary, RawTerm, Term};
696    use keylist::Keylist;
697    use num_bigint::BigInt;
698    use std::collections::HashMap;
699
700    #[test]
701    fn mixed_list() {
702        use crate::Term::*;
703
704        let input = read_binary("bins/mixed_list.bin").unwrap();
705        let out = from_bytes(&input).unwrap();
706
707        assert_eq!(
708            List(vec![
709                Byte(1),
710                String("some".to_string()),
711                Byte(2),
712                String("text".to_string())
713            ]),
714            Term::from(out)
715        );
716    }
717
718    #[test]
719    fn number_list() {
720        use crate::Term::*;
721
722        let input = read_binary("bins/number_list.bin").unwrap();
723        let out = from_bytes(&input).unwrap();
724
725        assert_eq!(Charlist(vec![1, 2, 3, 4]), Term::from(out));
726    }
727
728    #[test]
729    fn binary() {
730        use crate::Term::*;
731
732        let input = read_binary("bins/binary.bin").unwrap();
733        let out = from_bytes(&input).unwrap();
734
735        assert_eq!(Bytes(vec![1, 2, 3, 4]), Term::from(out));
736    }
737
738    #[test]
739    fn binary_non_utf8() {
740        use crate::Term::*;
741
742        let input = read_binary("bins/non_utf8_string.bin").unwrap();
743        let out = from_bytes(&input).unwrap();
744
745        assert_eq!(
746            Bytes(vec![
747                143, 45, 211, 57, 243, 220, 73, 235, 239, 201, 232, 189, 101
748            ]),
749            Term::from(out)
750        );
751    }
752
753    #[test]
754    fn binary_utf8() {
755        use crate::Term::*;
756
757        let input = read_binary("bins/small_string.bin").unwrap();
758        let out = from_bytes(&input).unwrap();
759
760        assert_eq!(
761            String(std::string::String::from("just some text")),
762            Term::from(out)
763        );
764    }
765
766    #[test]
767    fn keyword() {
768        let input = read_binary("bins/keyword.bin").unwrap();
769        let out = from_bytes(&input).unwrap();
770
771        let mut expected = Keylist::new();
772        expected.push("just".to_string(), Term::String("some key".to_string()));
773        expected.push("other".to_string(), Term::String("value".to_string()));
774        expected.push("just".to_string(), Term::Int(1234));
775
776        assert_eq!(Term::Keyword(expected), Term::from(out));
777    }
778
779    #[test]
780    fn atom_map() {
781        let input = read_binary("bins/atom_map.bin").unwrap();
782        let out = from_bytes(&input).unwrap();
783
784        let mut expected = HashMap::new();
785        expected.insert(
786            Term::Atom("just".to_string()),
787            Term::String("some key".to_string()),
788        );
789        expected.insert(
790            Term::Atom("other".to_string()),
791            Term::String("value".to_string()),
792        );
793
794        assert_eq!(Term::Map(expected), Term::from(out));
795    }
796
797    #[test]
798    fn map() {
799        use std::iter::FromIterator;
800
801        let input = read_binary("bins/map.bin").unwrap();
802        let out = from_bytes(&input).unwrap();
803
804        let mut sub = HashMap::new();
805        sub.insert(Term::Atom("test".to_string()), Term::Bool(false));
806        let mut nested = HashMap::new();
807        nested.insert(Term::String("ok".to_string()), Term::List(Vec::new()));
808
809        let expected = HashMap::from_iter(vec![
810            (Term::Byte(1), Term::String("one".to_string())),
811            (
812                Term::Atom("tuple".to_string()),
813                Term::Tuple(vec![Term::Byte(1), Term::Atom("more".to_string())]),
814            ),
815            (
816                Term::List(vec![Term::String("list as a key".to_string())]),
817                Term::List(vec![Term::String("another".to_string()), Term::Map(sub)]),
818            ),
819            (Term::String("float".to_string()), Term::Float(3.14.into())),
820            (
821                Term::String("large".to_string()),
822                Term::BigInt(BigInt::parse_bytes(b"123456789123456789", 10).unwrap()),
823            ),
824            (Term::String("nested".to_string()), Term::Map(nested)),
825        ]);
826
827        assert_eq!(Term::Map(expected), Term::from(out));
828    }
829
830    #[test]
831    fn nil() {
832        let out = RawTerm::Atom("nil".to_string());
833        assert_eq!(Term::Nil, Term::from(out));
834    }
835
836    #[test]
837    fn false_test() {
838        let out = RawTerm::Atom("false".to_string());
839        assert_eq!(Term::Bool(false), Term::from(out));
840    }
841
842    #[test]
843    fn true_test() {
844        let out = RawTerm::Atom("true".to_string());
845        assert_eq!(Term::Bool(true), Term::from(out));
846    }
847}
848
849#[cfg(test)]
850mod from_tests {
851    use crate::Term;
852    use keylist::Keylist;
853    use num_bigint::BigInt;
854    use std::collections::HashMap;
855    use std::iter::FromIterator;
856
857    #[test]
858    fn from_i32() {
859        assert_eq!(Term::Int(12345), 12345i32.into())
860    }
861
862    #[test]
863    fn from_u8() {
864        assert_eq!(Term::Byte(3), 3u8.into())
865    }
866
867    #[test]
868    fn from_u32() {
869        assert_eq!(Term::BigInt(BigInt::from(12345)), 12345u32.into())
870    }
871
872    #[test]
873    fn from_i128() {
874        assert_eq!(
875            Term::BigInt(BigInt::from(1111111111112345i128)),
876            1111111111112345i128.into()
877        )
878    }
879
880    #[test]
881    fn from_str() {
882        assert_eq!(Term::String(String::from("test")), "test".into())
883    }
884
885    #[test]
886    fn from_string() {
887        assert_eq!(
888            Term::String(String::from("test")),
889            String::from("test").into()
890        )
891    }
892
893    #[test]
894    fn from_list_to_keyword() {
895        let input = vec![("test", 12), ("testing", 129)];
896        let expected = Term::Keyword(Keylist::from(vec![
897            ("test".to_string(), 12.into()),
898            ("testing".to_string(), 129.into()),
899        ]));
900        assert_eq!(expected, input.into())
901    }
902
903    #[test]
904    fn from_list_keyword_to_map() {
905        use Term::*;
906        let input = vec![(vec!["test"], 12), (vec!["testing"], 129)];
907        let expected = Term::Map(HashMap::from_iter(vec![
908            (List(vec!["test".into()]), 12.into()),
909            (List(vec!["testing".into()]), 129.into()),
910        ]));
911        assert_eq!(expected, input.into())
912    }
913
914    #[test]
915    fn from_list_nested() {
916        use Term::*;
917        let input = vec![vec![12], vec![1234]];
918        let expected = Term::List(vec![List(vec![12.into()]), List(vec![1234.into()])]);
919        assert_eq!(expected, input.into())
920    }
921
922    #[test]
923    fn from_list() {
924        let input = vec!["test", "testing", "more", "another"];
925        let expected = Term::List(vec![
926            "test".into(),
927            "testing".into(),
928            "more".into(),
929            "another".into(),
930        ]);
931        assert_eq!(expected, input.into())
932    }
933
934    #[test]
935    fn from_map() {
936        use std::collections::HashMap;
937        use std::iter::FromIterator;
938
939        let map = HashMap::from_iter(vec![(1u8, "test"), (5u8, "testing")]);
940
941        assert_eq!(
942            Term::Map(HashMap::from_iter(vec![
943                (1u8.into(), "test".into()),
944                (5u8.into(), "testing".into())
945            ])),
946            map.into()
947        );
948    }
949
950    #[test]
951    fn from_tuple_1() {
952        let expected = Term::Tuple(vec![0.into()]);
953
954        assert_eq!(expected, (0,).into())
955    }
956
957    #[test]
958    fn from_tuple_2() {
959        let expected = Term::Tuple(vec![0.into(), 1.into()]);
960
961        assert_eq!(expected, (0, 1).into())
962    }
963
964    #[test]
965    fn from_tuple_16() {
966        let expected = Term::Tuple((0..16).map(Term::from).collect());
967
968        assert_eq!(
969            expected,
970            (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).into()
971        )
972    }
973}
974
975#[cfg(test)]
976mod print {
977    use super::print_elixir_term;
978    use crate::{RawTerm, Term};
979    use keylist::Keylist;
980    use num_bigint::BigInt;
981    use std::collections::HashMap;
982    use std::iter::FromIterator;
983
984    #[test]
985    fn display() {
986        let keylist: Keylist<String, Term> = Keylist::from_iter(vec![
987            ("test".into(), 1.into()),
988            ("Test".into(), 2.into()),
989            ("1234Test".into(), 3.into()),
990        ]);
991        assert_eq!(
992            r#"[test: 1, Test: 2, "1234Test": 3]"#,
993            Term::Keyword(keylist).to_string()
994        );
995    }
996
997    #[test]
998    fn elixir_term() {
999        assert_eq!(
1000            "\"testing\"",
1001            print_elixir_term(&Term::String(String::from("testing")))
1002        );
1003        assert_eq!("123", print_elixir_term(&Term::Byte(123)));
1004        assert_eq!("nil", print_elixir_term(&Term::Nil));
1005        assert_eq!(
1006            ":testing",
1007            print_elixir_term(&Term::Atom(String::from("testing")))
1008        );
1009        assert_eq!(
1010            "NiceModule",
1011            print_elixir_term(&Term::Atom(String::from("NiceModule")))
1012        );
1013        assert_eq!(
1014            ":\":D:D\"",
1015            print_elixir_term(&Term::Atom(String::from(":D:D")))
1016        );
1017        assert_eq!(
1018            ":\"1234testing\"",
1019            print_elixir_term(&Term::Atom(String::from("1234testing")))
1020        );
1021        assert_eq!(
1022            "<<1, 2, 3, 4, 5, 6, 7>>",
1023            print_elixir_term(&Term::Bytes(vec![1, 2, 3, 4, 5, 6, 7]))
1024        );
1025        assert_eq!(
1026            "[1, 2, 3, 4, 5, 6, 7]",
1027            print_elixir_term(&Term::Charlist(vec![1, 2, 3, 4, 5, 6, 7]))
1028        );
1029        assert_eq!(
1030            "3.123124123123123",
1031            print_elixir_term(&Term::Float(3.123_124_123_123_123.into()))
1032        );
1033        assert_eq!("123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789", print_elixir_term(&Term::BigInt(BigInt::parse_bytes(b"123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789", 10).unwrap())));
1034        let keylist: Keylist<String, Term> = Keylist::from_iter(vec![
1035            ("test".into(), 1.into()),
1036            ("Test".into(), 2.into()),
1037            ("1234Test".into(), 3.into()),
1038        ]);
1039        assert_eq!(
1040            r#"[test: 1, Test: 2, "1234Test": 3]"#,
1041            print_elixir_term(&Term::Keyword(keylist))
1042        );
1043        let list = vec![
1044            "hallo".into(),
1045            Term::Byte(123),
1046            Term::Nil,
1047            Term::Bytes(vec![1, 2, 3, 4, 5, 6, 7]),
1048        ];
1049        assert_eq!(
1050            r#"["hallo", 123, nil, <<1, 2, 3, 4, 5, 6, 7>>]"#,
1051            print_elixir_term(&Term::List(list))
1052        );
1053        let list = vec![
1054            "hallo".into(),
1055            Term::Byte(123),
1056            Term::Nil,
1057            Term::Bytes(vec![1, 2, 3, 4, 5, 6, 7]),
1058        ];
1059        assert_eq!(
1060            r#"{"hallo", 123, nil, <<1, 2, 3, 4, 5, 6, 7>>}"#,
1061            print_elixir_term(&Term::Tuple(list))
1062        );
1063        let map: HashMap<Term, Term> = HashMap::from_iter(vec![
1064            ("test".into(), 1.into()),
1065            ("Test".into(), 2.into()),
1066            ("1234Test".into(), 3.into()),
1067            ("testing testing".into(), 4.into()),
1068        ]);
1069        let map_text = print_elixir_term(&Term::Map(map));
1070        assert!(map_text.contains("Test"));
1071        assert!(map_text.contains("%{"));
1072        assert!(map_text.contains(r#""1234Test" => 3"#));
1073
1074        let map = HashMap::from_iter(vec![
1075            (Term::List(vec!["test".into()]), 12.into()),
1076            (Term::List(vec!["testing".into()]), 129.into()),
1077        ]);
1078
1079        let map_text = print_elixir_term(&Term::Map(map));
1080        assert!(map_text.contains(r#"["test"] => 12"#));
1081        assert!(map_text.contains(r#"["testing"] => 129"#));
1082        assert!(map_text.contains("%{"));
1083
1084        assert_eq!(
1085            "#Other(Int(-123))",
1086            print_elixir_term(&Term::Other(RawTerm::Int(-123)))
1087        );
1088    }
1089}
1090
1091#[cfg(all(test, feature = "serde_impl"))]
1092mod serde_tests {
1093    use crate::{RawTerm, Term};
1094    #[test]
1095    fn json_round_trip() {
1096        let input = r#"
1097        {
1098            "number": 1,
1099            "float": 3.14,
1100            "list": [1,2,3,4, {"nested": "true"}],
1101            "map": {
1102                "test": 123456
1103            },
1104            "string": "testing",
1105            "boolean": true,
1106            "none": null
1107        }
1108        "#;
1109        let value: serde_json::Value = serde_json::from_str(input).unwrap();
1110        let term: Term = serde_json::from_value(value.clone()).unwrap();
1111        let raw_term = RawTerm::from(term);
1112        let bytes = raw_term.to_bytes();
1113        let new_raw_term = RawTerm::from_bytes(&bytes).unwrap();
1114        let new_term = Term::from(new_raw_term);
1115        let json = serde_json::to_value(&new_term).unwrap();
1116
1117        assert_eq!(value, json);
1118    }
1119
1120    #[test]
1121    fn elixir_round_trip() {
1122        // r#"
1123        // [
1124        //     number: 1,
1125        //     float: 3.14,
1126        //     list: [1,2,3,4, %{"nested" => "true"}],
1127        //     map: %{
1128        //         "test" => 123456
1129        //     },
1130        //     string: "testing",
1131        //     boolean: true,
1132        //     none: nil
1133        // ]
1134        // "#;
1135
1136        let input = vec![
1137            131, 108, 0, 0, 0, 7, 104, 2, 100, 0, 6, 110, 117, 109, 98, 101, 114, 97, 1, 104, 2,
1138            100, 0, 5, 102, 108, 111, 97, 116, 70, 64, 9, 30, 184, 81, 235, 133, 31, 104, 2, 100,
1139            0, 4, 108, 105, 115, 116, 108, 0, 0, 0, 5, 97, 1, 97, 2, 97, 3, 97, 4, 116, 0, 0, 0, 1,
1140            109, 0, 0, 0, 6, 110, 101, 115, 116, 101, 100, 109, 0, 0, 0, 4, 116, 114, 117, 101,
1141            106, 104, 2, 100, 0, 3, 109, 97, 112, 116, 0, 0, 0, 1, 109, 0, 0, 0, 4, 116, 101, 115,
1142            116, 98, 0, 1, 226, 64, 104, 2, 100, 0, 6, 115, 116, 114, 105, 110, 103, 109, 0, 0, 0,
1143            7, 116, 101, 115, 116, 105, 110, 103, 104, 2, 100, 0, 7, 98, 111, 111, 108, 101, 97,
1144            110, 100, 0, 4, 116, 114, 117, 101, 104, 2, 100, 0, 4, 110, 111, 110, 101, 100, 0, 3,
1145            110, 105, 108, 106,
1146        ];
1147
1148        // because deserialized values will never be atoms:
1149        // r#"
1150        // [
1151        //     {"number", 1},
1152        //     {"float", 3.14},
1153        //     {"list", [1, 2, 3, 4, %{"nested" => "true"}]},
1154        //     {"map", %{"test" => 123456}},
1155        //     {"string", "testing"},
1156        //     {"boolean", true},
1157        //     {"none", nil}
1158        // ]
1159        // "#;
1160
1161        let adjusted_input = vec![
1162            131, 108, 0, 0, 0, 7, 104, 2, 109, 0, 0, 0, 6, 110, 117, 109, 98, 101, 114, 97, 1, 104,
1163            2, 109, 0, 0, 0, 5, 102, 108, 111, 97, 116, 70, 64, 9, 30, 184, 81, 235, 133, 31, 104,
1164            2, 109, 0, 0, 0, 4, 108, 105, 115, 116, 108, 0, 0, 0, 5, 97, 1, 97, 2, 97, 3, 97, 4,
1165            116, 0, 0, 0, 1, 109, 0, 0, 0, 6, 110, 101, 115, 116, 101, 100, 109, 0, 0, 0, 4, 116,
1166            114, 117, 101, 106, 104, 2, 109, 0, 0, 0, 3, 109, 97, 112, 116, 0, 0, 0, 1, 109, 0, 0,
1167            0, 4, 116, 101, 115, 116, 98, 0, 1, 226, 64, 104, 2, 109, 0, 0, 0, 6, 115, 116, 114,
1168            105, 110, 103, 109, 0, 0, 0, 7, 116, 101, 115, 116, 105, 110, 103, 104, 2, 109, 0, 0,
1169            0, 7, 98, 111, 111, 108, 101, 97, 110, 119, 4, 116, 114, 117, 101, 104, 2, 109, 0, 0,
1170            0, 4, 110, 111, 110, 101, 119, 3, 110, 105, 108, 106,
1171        ];
1172
1173        let term = Term::from_bytes(&adjusted_input).unwrap();
1174        let json = serde_json::to_value(&term).unwrap();
1175        let new_term: Term = serde_json::from_value(json).unwrap();
1176        let output = new_term.to_bytes();
1177
1178        assert_eq!(adjusted_input, output);
1179
1180        // bonus: the input will be transformed to adjusted input
1181        let term = Term::from_bytes(&input).unwrap();
1182        let json = serde_json::to_value(&term).unwrap();
1183        let new_term: Term = serde_json::from_value(json).unwrap();
1184        let output = new_term.to_bytes();
1185
1186        assert_eq!(adjusted_input, output);
1187    }
1188}