1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use serde::{Deserialize, Serialize};

#[cfg(feature = "test_utils")]
use arbitrary::{self, Arbitrary};

// Internal constants
const OUTPUT_BUILTIN_NAME: &str = "output";
const HASH_BUILTIN_NAME: &str = "pedersen";
const RANGE_CHECK_BUILTIN_NAME: &str = "range_check";
const RANGE_CHECK_96_BUILTIN_NAME: &str = "range_check96";
const SIGNATURE_BUILTIN_NAME: &str = "ecdsa";
const BITWISE_BUILTIN_NAME: &str = "bitwise";
const EC_OP_BUILTIN_NAME: &str = "ec_op";
const KECCAK_BUILTIN_NAME: &str = "keccak";
const POSEIDON_BUILTIN_NAME: &str = "poseidon";
const SEGMENT_ARENA_BUILTIN_NAME: &str = "segment_arena";
const ADD_MOD_BUILTIN_NAME: &str = "add_mod";
const MUL_MOD_BUILTIN_NAME: &str = "mul_mod";

const OUTPUT_BUILTIN_NAME_WITH_SUFFIX: &str = "output_builtin";
const HASH_BUILTIN_NAME_WITH_SUFFIX: &str = "pedersen_builtin";
const RANGE_CHECK_BUILTIN_NAME_WITH_SUFFIX: &str = "range_check_builtin";
const RANGE_CHECK_96_BUILTIN_NAME_WITH_SUFFIX: &str = "range_check96_builtin";
const SIGNATURE_BUILTIN_NAME_WITH_SUFFIX: &str = "ecdsa_builtin";
const BITWISE_BUILTIN_NAME_WITH_SUFFIX: &str = "bitwise_builtin";
const EC_OP_BUILTIN_NAME_WITH_SUFFIX: &str = "ec_op_builtin";
const KECCAK_BUILTIN_NAME_WITH_SUFFIX: &str = "keccak_builtin";
const POSEIDON_BUILTIN_NAME_WITH_SUFFIX: &str = "poseidon_builtin";
const SEGMENT_ARENA_BUILTIN_NAME_WITH_SUFFIX: &str = "segment_arena_builtin";
const ADD_MOD_BUILTIN_NAME_WITH_SUFFIX: &str = "add_mod_builtin";
const MUL_MOD_BUILTIN_NAME_WITH_SUFFIX: &str = "mul_mod_builtin";

/// Enum representing the name of a cairo builtin
#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum BuiltinName {
    output,
    range_check,
    pedersen,
    ecdsa,
    keccak,
    bitwise,
    ec_op,
    poseidon,
    segment_arena,
    range_check96,
    add_mod,
    mul_mod,
}

impl BuiltinName {
    /// Converts a [`BuiltinName`] to its string representation adding the "_builtin" suffix
    ///
    /// ## Example
    ///
    /// ```
    /// # use cairo_vm::types::builtin_name::BuiltinName;
    ///
    /// let builtin_name = BuiltinName::poseidon;
    /// assert_eq!(builtin_name.to_str_with_suffix(), "poseidon_builtin");
    ///
    /// ```
    pub fn to_str_with_suffix(self) -> &'static str {
        match self {
            BuiltinName::output => OUTPUT_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::range_check => RANGE_CHECK_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::pedersen => HASH_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::ecdsa => SIGNATURE_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::keccak => KECCAK_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::bitwise => BITWISE_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::ec_op => EC_OP_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::poseidon => POSEIDON_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::segment_arena => SEGMENT_ARENA_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::range_check96 => RANGE_CHECK_96_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::add_mod => ADD_MOD_BUILTIN_NAME_WITH_SUFFIX,
            BuiltinName::mul_mod => MUL_MOD_BUILTIN_NAME_WITH_SUFFIX,
        }
    }

    /// Converts a [`BuiltinName`] to its string representation
    ///
    /// ## Example
    ///
    /// ```
    /// # use cairo_vm::types::builtin_name::BuiltinName;
    ///
    /// let builtin_name = BuiltinName::poseidon;
    /// assert_eq!(builtin_name.to_str(), "poseidon");
    ///
    /// ```
    pub fn to_str(self) -> &'static str {
        match self {
            BuiltinName::output => OUTPUT_BUILTIN_NAME,
            BuiltinName::range_check => RANGE_CHECK_BUILTIN_NAME,
            BuiltinName::pedersen => HASH_BUILTIN_NAME,
            BuiltinName::ecdsa => SIGNATURE_BUILTIN_NAME,
            BuiltinName::keccak => KECCAK_BUILTIN_NAME,
            BuiltinName::bitwise => BITWISE_BUILTIN_NAME,
            BuiltinName::ec_op => EC_OP_BUILTIN_NAME,
            BuiltinName::poseidon => POSEIDON_BUILTIN_NAME,
            BuiltinName::segment_arena => SEGMENT_ARENA_BUILTIN_NAME,
            BuiltinName::range_check96 => RANGE_CHECK_96_BUILTIN_NAME,
            BuiltinName::add_mod => ADD_MOD_BUILTIN_NAME,
            BuiltinName::mul_mod => MUL_MOD_BUILTIN_NAME,
        }
    }

    /// Converts a [`BuiltinName`] from its string representation removing the "_builtin" suffix
    ///
    /// ## Example
    ///
    /// ```
    /// # use cairo_vm::types::builtin_name::BuiltinName;
    ///
    /// assert_eq!(BuiltinName::from_str_with_suffix("poseidon_builtin"), Some(BuiltinName::poseidon));
    ///
    /// assert_eq!(BuiltinName::from_str_with_suffix("unknown"), None);
    ///
    /// ```
    pub fn from_str_with_suffix(suffixed_str: &str) -> Option<Self> {
        match suffixed_str {
            OUTPUT_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::output),
            RANGE_CHECK_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::range_check),
            HASH_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::pedersen),
            SIGNATURE_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::ecdsa),
            KECCAK_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::keccak),
            BITWISE_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::bitwise),
            EC_OP_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::ec_op),
            POSEIDON_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::poseidon),
            SEGMENT_ARENA_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::segment_arena),
            RANGE_CHECK_96_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::range_check96),
            ADD_MOD_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::add_mod),
            MUL_MOD_BUILTIN_NAME_WITH_SUFFIX => Some(BuiltinName::mul_mod),
            _ => None,
        }
    }

    // Implementing this as a trait would generate confusion as `Display` impl uses suffixed version
    #[allow(clippy::should_implement_trait)]
    /// Converts a [`BuiltinName`] from its string representation
    ///
    /// ## Example
    ///
    /// ```
    /// # use cairo_vm::types::builtin_name::BuiltinName;
    ///
    /// assert_eq!(BuiltinName::from_str("poseidon"), Some(BuiltinName::poseidon));
    ///
    /// assert_eq!(BuiltinName::from_str("unknown"), None);
    ///
    /// ```
    pub fn from_str(str: &str) -> Option<Self> {
        match str {
            OUTPUT_BUILTIN_NAME => Some(BuiltinName::output),
            RANGE_CHECK_BUILTIN_NAME => Some(BuiltinName::range_check),
            HASH_BUILTIN_NAME => Some(BuiltinName::pedersen),
            SIGNATURE_BUILTIN_NAME => Some(BuiltinName::ecdsa),
            KECCAK_BUILTIN_NAME => Some(BuiltinName::keccak),
            BITWISE_BUILTIN_NAME => Some(BuiltinName::bitwise),
            EC_OP_BUILTIN_NAME => Some(BuiltinName::ec_op),
            POSEIDON_BUILTIN_NAME => Some(BuiltinName::poseidon),
            SEGMENT_ARENA_BUILTIN_NAME => Some(BuiltinName::segment_arena),
            RANGE_CHECK_96_BUILTIN_NAME => Some(BuiltinName::range_check96),
            ADD_MOD_BUILTIN_NAME => Some(BuiltinName::add_mod),
            MUL_MOD_BUILTIN_NAME => Some(BuiltinName::mul_mod),
            _ => None,
        }
    }
}

/// NOTE: Adds "_builtin" suffix
impl core::fmt::Display for BuiltinName {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.to_str_with_suffix().fmt(f)
    }
}

// Implementation of custom serialization & deserialization for maps using builtin names with suffixes as keys
pub(crate) mod serde_generic_map_impl {
    use super::BuiltinName;
    use crate::stdlib::{collections::HashMap, string::String};
    use serde::{de::Error, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};

    pub fn serialize<S, V>(
        values: &HashMap<BuiltinName, V>,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
        V: Serialize,
    {
        let mut map_serializer = serializer.serialize_map(Some(values.len()))?;
        for (key, val) in values {
            map_serializer.serialize_entry(key.to_str_with_suffix(), val)?
        }
        map_serializer.end()
    }

    pub fn deserialize<'de, D: Deserializer<'de>, V: Deserialize<'de>>(
        d: D,
    ) -> Result<HashMap<BuiltinName, V>, D::Error> {
        // First deserialize keys into String
        let map = HashMap::<String, V>::deserialize(d)?;
        // Then match keys to BuiltinName and handle invalid names
        map.into_iter()
            .map(|(k, v)| BuiltinName::from_str_with_suffix(&k).map(|k| (k, v)))
            .collect::<Option<HashMap<_, _>>>()
            .ok_or(D::Error::custom("Invalid builtin name"))
    }
}