gbf_core/
operand.rs

1#![deny(missing_docs)]
2
3use core::fmt;
4use serde::{Deserialize, Serialize};
5
6use thiserror::Error;
7
8use crate::utils::Gs2BytecodeAddress;
9
10/// Represents an error that can occur when parsing an operand.
11#[derive(Debug, Error, Clone, Serialize, Deserialize)]
12pub enum OperandError {
13    /// Invalid conversion
14    #[error("Attempted to convert {0} operand to a {1}")]
15    InvalidConversion(String, String),
16
17    /// Invalid jump target
18    #[error("Invalid jump target: {0}")]
19    InvalidJumpTarget(Gs2BytecodeAddress),
20}
21
22/// Represents an operand, which can be one of several types.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
24pub enum Operand {
25    /// A string operand.
26    String(String),
27
28    /// A floating-point operand (stored as a string).
29    Float(String),
30
31    /// An integer operand.
32    Number(i32),
33}
34
35impl Operand {
36    /// Creates a new string operand.
37    ///
38    /// # Arguments
39    /// - `value`: The value of the string operand.
40    ///
41    /// # Returns
42    /// - A new `Operand::String`.
43    ///
44    /// # Examples
45    /// ```
46    /// use gbf_core::operand::Operand;
47    ///
48    /// let operand = Operand::new_string("Hello, world!");
49    /// ```
50    pub fn new_string(value: impl Into<String>) -> Self {
51        Operand::String(value.into())
52    }
53
54    /// Creates a new float operand.
55    ///
56    /// # Arguments
57    /// - `value`: The value of the float operand.
58    ///
59    /// # Returns
60    /// - A new `Operand::Float`.
61    ///
62    /// # Examples
63    /// ```
64    /// use gbf_core::operand::Operand;
65    ///
66    /// let operand = Operand::new_float("3.14");
67    /// ```
68    pub fn new_float(value: impl Into<String>) -> Self {
69        Operand::Float(value.into())
70    }
71
72    /// Creates a new number operand.
73    ///
74    /// # Arguments
75    /// - `value`: The value of the number operand.
76    ///
77    /// # Returns
78    /// - A new `Operand::Number`.
79    ///
80    /// # Examples
81    /// ```
82    /// use gbf_core::operand::Operand;
83    ///
84    /// let operand = Operand::new_number(42);
85    /// ```
86    pub fn new_number(value: i32) -> Self {
87        Operand::Number(value)
88    }
89
90    /// Retrieves the value of the operand as a string reference, if applicable.
91    ///
92    /// # Returns
93    /// - The value of the operand as a string reference.
94    ///
95    /// # Errors
96    /// - `OperandError::InvalidConversion` if the operand is a number.
97    ///
98    /// # Examples
99    /// ```
100    /// use gbf_core::operand::Operand;
101    ///
102    /// let operand = Operand::new_string("Hello, world!");
103    /// let value = operand.get_string_value().unwrap();
104    /// assert_eq!(value, "Hello, world!");
105    /// ```
106    pub fn get_string_value(&self) -> Result<&str, OperandError> {
107        match self {
108            Operand::String(value) | Operand::Float(value) => Ok(value),
109            Operand::Number(_) => Err(OperandError::InvalidConversion(
110                "Number".to_string(),
111                "String".to_string(),
112            )),
113        }
114    }
115
116    /// Retrieves the value of the operand as a number, if applicable.
117    ///
118    /// # Returns
119    /// - The value of the operand as a number.
120    ///
121    /// # Errors
122    /// - `OperandError::InvalidConversion` if the operand is a string.
123    ///
124    /// # Examples
125    /// ```
126    /// use gbf_core::operand::Operand;
127    ///
128    /// let operand = Operand::new_number(42);
129    /// let value = operand.get_number_value().unwrap();
130    /// assert_eq!(value, 42);
131    /// ```
132    pub fn get_number_value(&self) -> Result<i32, OperandError> {
133        match self {
134            Operand::Number(value) => Ok(*value),
135            Operand::String(_) | Operand::Float(_) => Err(OperandError::InvalidConversion(
136                "String/Float".to_string(),
137                "Number".to_string(),
138            )),
139        }
140    }
141}
142
143impl fmt::Display for Operand {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        match self {
146            Operand::String(value) => value.clone(),
147            Operand::Float(value) => value.clone(),
148            Operand::Number(value) => format!("{:#x}", value),
149        }
150        .fmt(f)
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn string_operand() {
160        let operand = Operand::new_string("Hello, world!");
161        assert_eq!(operand.get_string_value().unwrap(), "Hello, world!");
162        assert_eq!(operand.to_string(), "Hello, world!");
163    }
164
165    #[test]
166    fn test_illegal_conversion() {
167        // Create number -> string
168        let operand = Operand::new_number(42);
169        assert!(operand.get_string_value().is_err());
170
171        // Create string -> number
172        let operand = Operand::new_string("Hello, world!");
173        assert!(operand.get_number_value().is_err());
174
175        // double -> number
176        let operand = Operand::new_float("3.14");
177        assert!(operand.get_number_value().is_err());
178    }
179
180    #[test]
181    fn float_operand() {
182        let operand = Operand::new_float("3.14");
183        assert_eq!(operand.get_string_value().unwrap(), "3.14");
184        assert_eq!(operand.to_string(), "3.14");
185    }
186
187    #[test]
188    fn int_operand() {
189        let operand = Operand::new_number(42);
190        assert_eq!(operand.get_number_value().unwrap(), 42);
191        assert_eq!(operand.to_string(), "0x2a");
192    }
193
194    #[test]
195    fn display_trait() {
196        let operand = Operand::new_number(123);
197        assert_eq!(operand.to_string(), "0x7b");
198        assert_eq!(format!("{}", operand), "0x7b");
199    }
200}