rustler/
dynamic.rs

1use std::ffi::c_double;
2
3#[cfg(feature = "nif_version_2_15")]
4use crate::sys::ErlNifTermType;
5
6use crate::wrapper::check;
7use crate::Term;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum TermType {
11    Atom,
12    Binary,
13    Fun,
14    List,
15    Map,
16    Integer,
17    Float,
18    Pid,
19    Port,
20    Ref,
21    Tuple,
22    Unknown,
23}
24
25#[cfg(feature = "nif_version_2_15")]
26impl From<ErlNifTermType> for TermType {
27    fn from(term_type: ErlNifTermType) -> Self {
28        use ErlNifTermType::*;
29        use TermType::*;
30        match term_type {
31            ERL_NIF_TERM_TYPE_ATOM => Atom,
32            ERL_NIF_TERM_TYPE_BITSTRING => Binary,
33            ERL_NIF_TERM_TYPE_FLOAT => Float,
34            ERL_NIF_TERM_TYPE_FUN => Fun,
35            ERL_NIF_TERM_TYPE_INTEGER => Integer,
36            ERL_NIF_TERM_TYPE_LIST => List,
37            ERL_NIF_TERM_TYPE_MAP => Map,
38            ERL_NIF_TERM_TYPE_PID => Pid,
39            ERL_NIF_TERM_TYPE_PORT => Port,
40            ERL_NIF_TERM_TYPE_REFERENCE => Ref,
41            ERL_NIF_TERM_TYPE_TUPLE => Tuple,
42            _ => Unknown,
43        }
44    }
45}
46
47pub fn get_type(term: Term) -> TermType {
48    if cfg!(feature = "nif_version_2_15") {
49        term.get_erl_type().into()
50    } else if term.is_atom() {
51        TermType::Atom
52    } else if term.is_binary() {
53        TermType::Binary
54    } else if term.is_fun() {
55        TermType::Fun
56    } else if term.is_list() || term.is_empty_list() {
57        TermType::List
58    } else if term.is_map() {
59        TermType::Map
60    } else if term.is_number() {
61        if term.is_float() {
62            TermType::Float
63        } else {
64            TermType::Integer
65        }
66    } else if term.is_pid() {
67        TermType::Pid
68    } else if term.is_port() {
69        TermType::Port
70    } else if term.is_ref() {
71        TermType::Ref
72    } else if term.is_tuple() {
73        TermType::Tuple
74    } else {
75        TermType::Unknown
76    }
77}
78
79macro_rules! impl_check {
80    ($check_fun:ident) => {
81        pub fn $check_fun(self) -> bool {
82            unsafe { check::$check_fun(self.get_env().as_c_arg(), self.as_c_arg()) }
83        }
84    };
85}
86
87/// ## Type checks
88impl Term<'_> {
89    /// Returns an enum representing which type the term is.
90    /// Note that using the individual `is_*` functions is more
91    /// efficient for checking a single type.
92    pub fn get_type(self) -> TermType {
93        get_type(self)
94    }
95
96    impl_check!(is_atom);
97    impl_check!(is_binary);
98    impl_check!(is_empty_list);
99    impl_check!(is_fun);
100    impl_check!(is_list);
101    impl_check!(is_map);
102    impl_check!(is_number);
103    impl_check!(is_pid);
104    impl_check!(is_port);
105    impl_check!(is_ref);
106    impl_check!(is_tuple);
107
108    pub fn is_float(self) -> bool {
109        let mut val: c_double = 0.0;
110        unsafe {
111            crate::sys::enif_get_double(self.get_env().as_c_arg(), self.as_c_arg(), &mut val) == 1
112        }
113    }
114
115    pub fn is_integer(self) -> bool {
116        self.is_number() && !self.is_float()
117    }
118}