Skip to main content

ruby_types/
lib.rs

1//! Ruby types for Rusty Ruby
2//!
3//! This crate provides core Ruby value and error types for the Rusty Ruby ecosystem.
4
5#![warn(missing_docs)]
6
7use serde::{Deserialize, Serialize};
8
9/// Ruby value type
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub enum RubyValue {
12    /// Nil value
13    Nil,
14    /// Boolean value
15    Boolean(bool),
16    /// Integer value
17    Integer(i32),
18    /// Float value
19    Float(f64),
20    /// String value
21    String(String),
22    /// Symbol value
23    Symbol(String),
24    /// Array value
25    Array(Vec<RubyValue>),
26    /// Hash value
27    Hash(std::collections::HashMap<String, RubyValue>),
28    /// Object value
29    Object(String, std::collections::HashMap<String, RubyValue>),
30}
31
32impl RubyValue {
33    /// Check if value is nil
34    pub fn is_nil(&self) -> bool {
35        matches!(self, RubyValue::Nil)
36    }
37
38    /// Convert value to i32
39    pub fn to_i32(&self) -> i32 {
40        match self {
41            RubyValue::Integer(i) => *i,
42            RubyValue::Float(f) => *f as i32,
43            RubyValue::Boolean(b) => {
44                if *b {
45                    1
46                }
47                else {
48                    0
49                }
50            }
51            _ => 0,
52        }
53    }
54
55    /// Convert value to f64
56    pub fn to_f64(&self) -> f64 {
57        match self {
58            RubyValue::Float(f) => *f,
59            RubyValue::Integer(i) => *i as f64,
60            RubyValue::Boolean(b) => {
61                if *b {
62                    1.0
63                }
64                else {
65                    0.0
66                }
67            }
68            _ => 0.0,
69        }
70    }
71
72    /// Convert value to bool
73    pub fn to_bool(&self) -> bool {
74        match self {
75            RubyValue::Nil => false,
76            RubyValue::Boolean(b) => *b,
77            RubyValue::Integer(i) => *i != 0,
78            RubyValue::Float(f) => *f != 0.0,
79            _ => true,
80        }
81    }
82
83    /// Convert value to string
84    pub fn to_string(&self) -> String {
85        match self {
86            RubyValue::String(s) => s.clone(),
87            RubyValue::Symbol(s) => s.clone(),
88            RubyValue::Integer(i) => i.to_string(),
89            RubyValue::Float(f) => f.to_string(),
90            RubyValue::Boolean(b) => b.to_string(),
91            RubyValue::Nil => "nil".to_string(),
92            _ => format!("{:?}", self),
93        }
94    }
95
96    /// Mark this value and all referenced values
97    pub fn mark(&self, marked: &mut std::collections::HashSet<*const RubyValue>) {
98        let ptr = self as *const RubyValue;
99        if marked.contains(&ptr) {
100            return;
101        }
102        marked.insert(ptr);
103
104        match self {
105            RubyValue::Array(values) => {
106                for value in values {
107                    value.mark(marked);
108                }
109            }
110            RubyValue::Hash(map) => {
111                for (_, value) in map {
112                    value.mark(marked);
113                }
114            }
115            RubyValue::Object(_, fields) => {
116                for (_, value) in fields {
117                    value.mark(marked);
118                }
119            }
120            _ => {}
121        }
122    }
123
124    /// Concatenate two Ruby values
125    pub fn concat(&self, other: &RubyValue) -> RubyValue {
126        match (self, other) {
127            (RubyValue::String(s1), RubyValue::String(s2)) => RubyValue::String(format!("{}{}", s1, s2)),
128            (RubyValue::Array(arr1), RubyValue::Array(arr2)) => {
129                let mut result = arr1.clone();
130                result.extend(arr2.clone());
131                RubyValue::Array(result)
132            }
133            _ => RubyValue::String(format!("{}{}", self.to_string(), other.to_string())),
134        }
135    }
136
137    /// Get array length
138    pub fn array_length(&self) -> Option<usize> {
139        match self {
140            RubyValue::Array(arr) => Some(arr.len()),
141            _ => None,
142        }
143    }
144
145    /// Get array element at index
146    pub fn array_get(&self, index: usize) -> Option<RubyValue> {
147        match self {
148            RubyValue::Array(arr) => arr.get(index).cloned(),
149            _ => None,
150        }
151    }
152
153    /// Set array element at index
154    pub fn array_set(&mut self, index: usize, value: RubyValue) -> Option<()> {
155        match self {
156            RubyValue::Array(arr) => {
157                if index < arr.len() {
158                    arr[index] = value;
159                    Some(())
160                }
161                else {
162                    None
163                }
164            }
165            _ => None,
166        }
167    }
168
169    /// Get hash value for key
170    pub fn hash_get(&self, key: &str) -> Option<RubyValue> {
171        match self {
172            RubyValue::Hash(map) => map.get(key).cloned(),
173            _ => None,
174        }
175    }
176
177    /// Set hash value for key
178    pub fn hash_set(&mut self, key: &str, value: RubyValue) -> Option<()> {
179        match self {
180            RubyValue::Hash(map) => {
181                map.insert(key.to_string(), value);
182                Some(())
183            }
184            _ => None,
185        }
186    }
187
188    /// Get hash keys
189    pub fn hash_keys(&self) -> Option<Vec<String>> {
190        match self {
191            RubyValue::Hash(map) => Some(map.keys().cloned().collect()),
192            _ => None,
193        }
194    }
195
196    /// Get hash values
197    pub fn hash_values(&self) -> Option<Vec<RubyValue>> {
198        match self {
199            RubyValue::Hash(map) => Some(map.values().cloned().collect()),
200            _ => None,
201        }
202    }
203
204    /// Get object field
205    pub fn object_get(&self, field: &str) -> Option<RubyValue> {
206        match self {
207            RubyValue::Object(_, fields) => fields.get(field).cloned(),
208            _ => None,
209        }
210    }
211
212    /// Set object field
213    pub fn object_set(&mut self, field: &str, value: RubyValue) -> Option<()> {
214        match self {
215            RubyValue::Object(_, fields) => {
216                fields.insert(field.to_string(), value);
217                Some(())
218            }
219            _ => None,
220        }
221    }
222}
223
224/// Ruby error type
225#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
226pub enum RubyError {
227    /// Method not found error
228    MethodNotFound(String),
229    /// Class not found error
230    ClassNotFound(String),
231    /// Lexical analysis error
232    LexicalError(String),
233    /// Syntax analysis error
234    SyntaxError(String),
235    /// Runtime error
236    RuntimeError(String),
237    /// Type error
238    TypeError(String),
239    /// Argument error
240    ArgumentError(String),
241}
242
243impl std::fmt::Display for RubyError {
244    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
245        match self {
246            RubyError::MethodNotFound(name) => write!(f, "Method not found: {}", name),
247            RubyError::ClassNotFound(name) => write!(f, "Class not found: {}", name),
248            RubyError::LexicalError(msg) => write!(f, "Lexical error: {}", msg),
249            RubyError::SyntaxError(msg) => write!(f, "Syntax error: {}", msg),
250            RubyError::RuntimeError(msg) => write!(f, "Runtime error: {}", msg),
251            RubyError::TypeError(msg) => write!(f, "Type error: {}", msg),
252            RubyError::ArgumentError(msg) => write!(f, "Argument error: {}", msg),
253        }
254    }
255}
256
257impl std::error::Error for RubyError {}
258
259/// Ruby result type
260pub type RubyResult<T> = std::result::Result<T, RubyError>;