1use std::{collections::VecDeque, fmt::Debug};
2
3use arc_gc::{
4 arc::{GCArc, GCArcWeak},
5 traceable::GCTraceable,
6};
7
8use crate::lambda::runnable::RuntimeError;
9
10use super::object::{OnionObject, OnionObjectCell, OnionStaticObject};
11
12#[derive(Clone)]
13pub struct OnionTuple {
14 elements: Vec<OnionObject>,
15}
16
17impl GCTraceable<OnionObjectCell> for OnionTuple {
18 fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
19 for element in &self.elements {
20 element.collect(queue);
21 }
22 }
23}
24
25impl Debug for OnionTuple {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self.elements.len() {
28 0 => write!(f, "()"),
29 1 => write!(f, "({:?},)", self.elements[0]),
30 _ => {
31 let elements: Vec<String> =
32 self.elements.iter().map(|e| format!("{:?}", e)).collect();
33 write!(f, "({})", elements.join(", "))
34 }
35 }
36 }
37}
38
39#[macro_export]
40macro_rules! onion_tuple {
41 ($($x:expr),*) => {
42 OnionTuple::new_static(vec![$($x),*])
43 };
44 () => {
45
46 };
47}
48
49impl OnionTuple {
50 pub fn new(elements: Vec<OnionObject>) -> Self {
51 OnionTuple {
52 elements: elements.into(),
53 }
54 }
55
56 pub fn new_static(elements: Vec<&OnionStaticObject>) -> OnionStaticObject {
57 OnionStaticObject::new(OnionObject::Tuple(
58 OnionTuple {
59 elements: elements
60 .into_iter()
61 .map(|e| e.weak().clone())
62 .collect::<Vec<_>>(),
63 }
64 .into(),
65 ))
66 }
67
68 pub fn new_static_no_ref(elements: &Vec<OnionStaticObject>) -> OnionStaticObject {
69 OnionObject::Tuple(
70 OnionTuple {
71 elements: elements
72 .into_iter()
73 .map(|e| e.weak().clone())
74 .collect::<Vec<_>>(),
75 }
76 .into(),
77 )
78 .consume_and_stabilize()
79 }
80
81 #[inline(always)]
82 pub fn get_elements(&self) -> &Vec<OnionObject> {
83 &self.elements
84 }
85
86 pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
87 self.elements.iter().for_each(|e| e.upgrade(collected));
88 }
89
90 pub fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
91 Ok(OnionStaticObject::new(OnionObject::Integer(
92 self.elements.len() as i64,
93 )))
94 }
95
96 pub fn at(&self, index: i64) -> Result<OnionStaticObject, RuntimeError> {
97 if index < 0 || index >= self.elements.len() as i64 {
98 return Err(RuntimeError::InvalidOperation(
99 format!("Index out of bounds: {}", index).into(),
100 ));
101 }
102 Ok(OnionStaticObject::new(
103 self.elements[index as usize].clone(),
104 ))
105 }
106
107 pub fn with_index<F, R>(&self, index: i64, f: &F) -> Result<R, RuntimeError>
108 where
109 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
110 {
111 if index < 0 || index >= self.elements.len() as i64 {
112 return Err(RuntimeError::InvalidOperation(
113 format!("Index out of bounds: {}", index).into(),
114 ));
115 }
116 let borrowed = &self.elements[index as usize];
117 f(borrowed)
118 }
119
120 pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
121 where
122 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
123 {
124 for element in &self.elements {
125 match element {
126 OnionObject::Named(named) => {
127 if named.get_key().equals(key)? {
128 return f(&named.get_value());
129 }
130 }
131 OnionObject::Pair(pair) => {
132 if pair.get_key().equals(key)? {
133 return f(&pair.get_value());
134 }
135 }
136 _ => {}
137 }
138 }
139 Err(RuntimeError::InvalidOperation(
140 format!("Attribute {:?} not found in tuple", key).into(),
141 ))
142 }
143
144 pub fn binary_add(&self, other: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
145 match other {
146 OnionObject::Tuple(other_tuple) => {
147 let mut new_elements = self.elements.clone();
148 new_elements.extend(other_tuple.elements.clone());
149 Ok(OnionStaticObject::new(OnionObject::Tuple(
150 OnionTuple {
151 elements: new_elements,
152 }
153 .into(),
154 )))
155 }
156 _ => Ok(OnionStaticObject::new(OnionObject::Undefined(Some(
157 format!("Cannot add tuple with {:?}", other).into(),
158 )))),
159 }
160 }
161
162 pub fn contains(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
163 for element in &self.elements {
164 if element.equals(other)? {
165 return Ok(true);
166 }
167 }
168 Ok(false)
169 }
170}
171
172impl OnionTuple {
173 pub fn equals(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
174 match other {
175 OnionObject::Tuple(other_tuple) => {
176 if self.elements.len() != other_tuple.elements.len() {
177 return Ok(false);
178 }
179 for (a, b) in self.elements.iter().zip(&other_tuple.elements) {
180 if a.equals(b)? {
181 return Ok(false);
182 }
183 }
184 Ok(true)
185 }
186 _ => Ok(false),
187 }
188 }
189}
190
191impl OnionTuple {
192 pub fn reconstruct_container(&self) -> Result<OnionObject, RuntimeError> {
193 let mut cloned_elements = Vec::with_capacity(self.elements.len());
194 for element in &self.elements {
195 cloned_elements.push(element.clone());
196 }
197 Ok(OnionObject::Tuple(
198 OnionTuple {
199 elements: cloned_elements,
200 }
201 .into(),
202 ))
203 }
204}