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