1use std::{collections::VecDeque, fmt::Debug, sync::Arc};
13
14use arc_gc::{
15 arc::{GCArc, GCArcWeak},
16 traceable::GCTraceable,
17};
18
19use crate::lambda::runnable::RuntimeError;
20
21use super::object::{OnionObject, OnionObjectCell, OnionStaticObject};
22
23#[derive(Clone)]
30pub struct OnionTuple {
31 elements: Arc<[OnionObject]>,
32}
33
34impl GCTraceable<OnionObjectCell> for OnionTuple {
35 fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
36 for element in self.elements.as_ref() {
37 element.collect(queue);
38 }
39 }
40}
41
42impl Debug for OnionTuple {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self.elements.len() {
45 0 => write!(f, "()"),
46 1 => write!(f, "({:?},)", self.elements[0]),
47 _ => {
48 let elements: Vec<String> =
49 self.elements.iter().map(|e| format!("{:?}", e)).collect();
50 write!(f, "({})", elements.join(", "))
51 }
52 }
53 }
54}
55
56#[macro_export]
57macro_rules! onion_tuple {
58 ($($x:expr),*) => {
59 OnionTuple::new_static(vec![$($x),*])
60 };
61 () => {
62
63 };
64}
65
66impl OnionTuple {
67 pub fn new(elements: Vec<OnionObject>) -> Self {
75 OnionTuple {
76 elements: elements.into(),
77 }
78 }
79
80 pub fn new_static(elements: Vec<&OnionStaticObject>) -> OnionStaticObject {
88 OnionStaticObject::new(OnionObject::Tuple(
89 OnionTuple {
90 elements: elements
91 .into_iter()
92 .map(|e| e.weak().clone())
93 .collect::<Vec<_>>()
94 .into(),
95 }
96 .into(),
97 ))
98 }
99
100 pub fn new_static_no_ref(elements: &Vec<OnionStaticObject>) -> OnionStaticObject {
108 OnionObject::Tuple(
109 OnionTuple {
110 elements: elements
111 .into_iter()
112 .map(|e| e.weak().clone())
113 .collect::<Vec<_>>()
114 .into(),
115 }
116 .into(),
117 )
118 .consume_and_stabilize()
119 }
120
121 pub fn new_from_slice(elements: &[OnionStaticObject]) -> OnionStaticObject {
129 OnionObject::Tuple(
130 OnionTuple {
131 elements: elements
132 .iter()
133 .map(|e| e.weak().clone())
134 .collect::<Vec<_>>()
135 .into(),
136 }
137 .into(),
138 )
139 .consume_and_stabilize()
140 }
141
142 #[inline(always)]
144 pub fn get_elements(&self) -> &[OnionObject] {
145 self.elements.as_ref()
146 }
147
148 pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
152 self.elements.iter().for_each(|e| e.upgrade(collected));
153 }
154
155 pub fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
160 Ok(OnionStaticObject::new(OnionObject::Integer(
161 self.elements.len() as i64,
162 )))
163 }
164
165 pub fn at(&self, index: i64) -> Result<OnionStaticObject, RuntimeError> {
176 if index < 0 || index >= self.elements.len() as i64 {
177 return Err(RuntimeError::InvalidOperation(
178 format!("Index out of bounds: {}", index).into(),
179 ));
180 }
181 Ok(OnionStaticObject::new(
182 self.elements[index as usize].clone(),
183 ))
184 }
185
186 pub fn with_index<F, R>(&self, index: i64, f: &F) -> Result<R, RuntimeError>
198 where
199 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
200 {
201 if index < 0 || index >= self.elements.len() as i64 {
202 return Err(RuntimeError::InvalidOperation(
203 format!("Index out of bounds: {}", index).into(),
204 ));
205 }
206 let borrowed = &self.elements[index as usize];
207 f(borrowed)
208 }
209
210 pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
220 where
221 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
222 {
223 for element in self.elements.as_ref() {
224 match element {
225 OnionObject::Pair(pair) => {
226 if pair.get_key().equals(key)? {
227 return f(&pair.get_value());
228 }
229 }
230 _ => {}
231 }
232 }
233 Err(RuntimeError::InvalidOperation(
234 format!("Attribute {:?} not found in tuple", key).into(),
235 ))
236 }
237
238 pub fn binary_add(&self, other: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
246 match other {
247 OnionObject::Tuple(other_tuple) => {
248 let new_elements: Arc<[OnionObject]> = self
249 .elements
250 .iter()
251 .chain(other_tuple.elements.iter())
252 .cloned()
253 .collect();
254 Ok(OnionStaticObject::new(OnionObject::Tuple(
255 OnionTuple {
256 elements: new_elements,
257 }
258 .into(),
259 )))
260 }
261 _ => Ok(OnionStaticObject::new(OnionObject::Undefined(Some(
262 format!("Cannot add tuple with {:?}", other).into(),
263 )))),
264 }
265 }
266
267 pub fn contains(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
276 for element in self.elements.as_ref() {
277 if element.equals(other)? {
278 return Ok(true);
279 }
280 }
281 Ok(false)
282 }
283}
284
285impl OnionTuple {
286 pub fn equals(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
287 match other {
288 OnionObject::Tuple(other_tuple) => {
289 if self.elements.len() != other_tuple.elements.len() {
290 return Ok(false);
291 }
292 for (a, b) in self.elements.iter().zip(other_tuple.elements.as_ref()) {
293 if a.equals(b)? {
294 return Ok(false);
295 }
296 }
297 Ok(true)
298 }
299 _ => Ok(false),
300 }
301 }
302}