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}