1use std::{collections::VecDeque, fmt::Debug};
2
3use arc_gc::{
4 arc::{GCArc, GCArcWeak},
5 gc::GC,
6 traceable::GCTraceable,
7};
8
9use crate::{
10 lambda::runnable::{Runnable, RuntimeError, StepResult},
11 onion_tuple,
12 types::lambda::launcher::OnionLambdaRunnableLauncher,
13 unwrap_step_result,
14};
15
16use super::{
17 lambda::definition::{LambdaBody, OnionLambdaDefinition},
18 object::{OnionObject, OnionObjectCell, OnionStaticObject},
19 tuple::OnionTuple,
20};
21
22pub struct OnionLazySet {
23 container: OnionObject,
24 filter: OnionObject,
25}
26
27impl GCTraceable<OnionObjectCell> for OnionLazySet {
28 fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
29 self.container.collect(queue);
30 self.filter.collect(queue);
31 }
32}
33
34impl Debug for OnionLazySet {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "LazySet({:?}, {:?})", self.container, self.filter)
37 }
38}
39
40impl OnionLazySet {
41 pub fn new(container: OnionObject, filter: OnionObject) -> Self {
42 OnionLazySet {
43 container: container.into(),
44 filter: filter.into(),
45 }
46 }
47
48 pub fn new_static(
49 container: &OnionStaticObject,
50 filter: &OnionStaticObject,
51 ) -> OnionStaticObject {
52 OnionObject::LazySet(
53 OnionLazySet {
54 container: container.weak().clone(),
55 filter: filter.weak().clone(),
56 }
57 .into(),
58 )
59 .stabilize()
60 }
61
62 #[inline(always)]
63 pub fn get_container(&self) -> &OnionObject {
64 &self.container
65 }
66
67 #[inline(always)]
68 pub fn get_filter(&self) -> &OnionObject {
69 &self.filter
70 }
71
72 pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
73 self.container.upgrade(collected);
74 self.filter.upgrade(collected)
75 }
76
77 pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
78 where
79 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
80 {
81 match key {
82 OnionObject::String(s) if s.as_str() == "container" => f(&self.container),
83 OnionObject::String(s) if s.as_str() == "filter" => f(&self.filter),
84 OnionObject::String(s) if s.as_str() == "collect" => {
85 let collector = OnionLazySetCollector {
86 container: self.container.stabilize(),
87 filter: self.filter.stabilize(),
88 collected: Vec::new(),
89 current_index: 0,
90 };
91 let collector = OnionLambdaDefinition::new_static(
92 &onion_tuple!(),
93 LambdaBody::NativeFunction(Box::new(collector)),
94 None,
95 None,
96 "collector".to_string(),
97 );
98 let result = {
100 let collector_weak = collector.weak();
101 f(collector_weak)
102 };
103 result
104 }
105 _ => Err(RuntimeError::InvalidOperation(
106 format!("Attribute '{:?}' not found in lazy set", key).into(),
107 )),
108 }
109 }
110}
111
112#[derive(Clone)]
113pub struct OnionLazySetCollector {
114 pub(crate) container: OnionStaticObject,
115 pub(crate) filter: OnionStaticObject,
116 pub(crate) collected: Vec<OnionStaticObject>,
117 pub(crate) current_index: usize,
118}
119
120impl Runnable for OnionLazySetCollector {
121 fn copy(&self) -> Box<dyn Runnable> {
122 Box::new(OnionLazySetCollector {
123 container: self.container.clone(),
124 filter: self.filter.clone(),
125 collected: self.collected.clone(),
126 current_index: self.current_index,
127 })
128 }
129
130 fn receive(
131 &mut self,
132 step_result: &StepResult,
133 _gc: &mut GC<OnionObjectCell>,
134 ) -> Result<(), RuntimeError> {
135 match step_result {
136 StepResult::Return(result) => {
137 match result.weak() {
138 OnionObject::Boolean(true) => {
139 match self.container.weak() {
140 OnionObject::Tuple(tuple) => {
141 if let Some(item) = tuple.get_elements().get(self.current_index - 1)
143 {
144 self.collected.push(item.stabilize());
145 Ok(())
146 } else {
147 Ok(())
149 }
150 }
151 _ => Err(RuntimeError::DetailedError(
152 "Container must be a tuple".to_string().into(),
153 )),
154 }
155 }
156 _ => {
157 Ok(())
159 }
160 }
161 }
162 StepResult::SetSelfObject(_) => {
163 Ok(())
166 }
167 _ => Err(RuntimeError::DetailedError(
168 "Unexpected step result in lazy set collector"
169 .to_string()
170 .into(),
171 )),
172 }
173 }
174
175 fn step(&mut self, _gc: &mut GC<OnionObjectCell>) -> StepResult {
176 unwrap_step_result!(self
177 .container
178 .weak()
179 .with_data(|container| match container {
180 OnionObject::Tuple(tuple) => {
181 if let Some(item) = tuple.get_elements().get(self.current_index) {
183 let item_clone = item.clone();
184 self.current_index += 1; self.filter
187 .weak()
188 .with_data(|filter: &OnionObject| match filter {
189 OnionObject::Lambda(_) => {
190 let argument = OnionObject::Tuple(
203 OnionTuple::new(vec![item_clone]).into(),
204 )
205 .consume_and_stabilize();
206 let runnable =
207 Box::new(OnionLambdaRunnableLauncher::new_static(
208 &self.filter,
209 &argument,
210 &|r| Ok(r),
211 )?);
212 Ok(StepResult::NewRunnable(runnable))
213 }
214 OnionObject::Boolean(false) => Ok(StepResult::Continue),
215 _ => {
216 self.collected.push(item_clone.consume_and_stabilize());
217 Ok(StepResult::Continue)
218 }
219 })
220 } else {
221 Ok(StepResult::Return(
223 OnionTuple::new_static_no_ref(&self.collected).into(),
224 ))
225 }
226 }
227 _ => Err(RuntimeError::InvalidType(
228 "Container must be a tuple".to_string().into(),
229 )),
230 }))
231 }
232
233 fn format_context(&self) -> Result<serde_json::Value, RuntimeError> {
234 return Ok(serde_json::json!({
235 "type": "LazySetCollector",
236 "container": self.container.to_string(),
237 "filter": self.filter.to_string(),
238 "collected": self.collected.iter().map(|o| o.to_string()).collect::<Vec<_>>(),
239 "current_index": self.current_index,
240 }));
241 }
242}
243
244impl OnionLazySet {
245 pub fn reconstruct_container(&self) -> Result<OnionObject, RuntimeError> {
246 Ok(OnionObject::LazySet(
247 OnionLazySet {
248 container: self.container.clone(),
249 filter: self.filter.clone(),
250 }
251 .into(),
252 ))
253 }
254}