1use std::{collections::VecDeque, fmt::Debug, sync::Arc};
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 types::lambda::{
12 definition::LambdaType, launcher::OnionLambdaRunnableLauncher, parameter::LambdaParameter,
13 },
14 unwrap_step_result,
15 utils::fastmap::{OnionFastMap, OnionKeyPool},
16};
17
18use super::{
19 lambda::definition::{LambdaBody, OnionLambdaDefinition},
20 object::{OnionObject, OnionObjectCell, OnionStaticObject},
21 tuple::OnionTuple,
22};
23
24pub struct OnionLazySet {
25 container: OnionObject,
26 filter: OnionObject,
27}
28
29impl GCTraceable<OnionObjectCell> for OnionLazySet {
30 fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
31 self.container.collect(queue);
32 self.filter.collect(queue);
33 }
34}
35
36impl Debug for OnionLazySet {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 write!(f, "LazySet({:?}, {:?})", self.container, self.filter)
39 }
40}
41
42impl OnionLazySet {
43 pub fn new(container: OnionObject, filter: OnionObject) -> Self {
44 OnionLazySet {
45 container: container.into(),
46 filter: filter.into(),
47 }
48 }
49
50 pub fn new_static(
51 container: &OnionStaticObject,
52 filter: &OnionStaticObject,
53 ) -> OnionStaticObject {
54 OnionObject::LazySet(
55 OnionLazySet {
56 container: container.weak().clone(),
57 filter: filter.weak().clone(),
58 }
59 .into(),
60 )
61 .stabilize()
62 }
63
64 #[inline(always)]
65 pub fn get_container(&self) -> &OnionObject {
66 &self.container
67 }
68
69 #[inline(always)]
70 pub fn get_filter(&self) -> &OnionObject {
71 &self.filter
72 }
73
74 pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
75 self.container.upgrade(collected);
76 self.filter.upgrade(collected)
77 }
78
79 pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
80 where
81 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
82 {
83 match key {
84 OnionObject::String(s) if s.as_ref() == "container" => f(&self.container),
85 OnionObject::String(s) if s.as_ref() == "filter" => f(&self.filter),
86 OnionObject::String(s) if s.as_ref() == "collect" => {
87 let empty_pool = OnionKeyPool::create(vec![]);
88 let collector = OnionLazySetCollector {
89 container: self.container.stabilize(),
90 filter: self.filter.stabilize(),
91 collected: Vec::new(),
92 current_index: 0,
93 };
94 let collector = OnionLambdaDefinition::new_static(
95 LambdaParameter::Multiple(Box::new([])),
96 LambdaBody::NativeFunction((
97 Arc::new({
98 let collector = collector.clone();
99 move |_, _, _, _| Box::new(collector.clone())
100 }),
101 empty_pool.clone(),
102 )),
103 OnionFastMap::new(empty_pool),
104 "collector".into(),
105 LambdaType::Normal,
106 );
107 let result = {
109 let collector_weak = collector.weak();
110 f(collector_weak)
111 };
112 result
113 }
114 _ => Err(RuntimeError::InvalidOperation(
115 format!("Attribute '{:?}' not found in lazy set", key).into(),
116 )),
117 }
118 }
119}
120
121#[derive(Clone)]
122pub struct OnionLazySetCollector {
123 pub(crate) container: OnionStaticObject,
124 pub(crate) filter: OnionStaticObject,
125 pub(crate) collected: Vec<OnionStaticObject>,
126 pub(crate) current_index: usize,
127}
128
129impl Runnable for OnionLazySetCollector {
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".into(),
153 )),
154 }
155 }
156 _ => {
157 Ok(())
159 }
160 }
161 }
162 _ => Err(RuntimeError::DetailedError(
163 "Unexpected step result in lazy set collector"
164 .to_string()
165 .into(),
166 )),
167 }
168 }
169 fn step(&mut self, _gc: &mut GC<OnionObjectCell>) -> StepResult {
170 unwrap_step_result!(
171 self.container
172 .weak()
173 .with_data(|container| match container {
174 OnionObject::Tuple(tuple) => {
175 if let Some(item) = tuple.get_elements().get(self.current_index) {
177 self.current_index += 1; self.filter
179 .weak()
180 .with_data(|filter: &OnionObject| match filter {
181 OnionObject::Lambda(_) => {
182 let runnable =
183 Box::new(OnionLambdaRunnableLauncher::new_static(
184 filter,
185 item.stabilize(),
186 &|r| Ok(r),
187 )?);
188 Ok(StepResult::NewRunnable(runnable))
189 }
190 v => {
191 if v.to_boolean()? {
192 self.collected.push(item.stabilize());
193 }
194 Ok(StepResult::Continue)
195 }
196 })
197 } else {
198 Ok(StepResult::Return(
200 OnionTuple::new_static_no_ref(&self.collected).into(),
201 ))
202 }
203 }
204 _ => Err(RuntimeError::InvalidType(
205 "Container must be a tuple".into(),
206 )),
207 })
208 )
209 }
210
211 fn format_context(&self) -> String {
212 let container_len = self
214 .container
215 .weak()
216 .with_data(|c| {
217 Ok(if let OnionObject::Tuple(t) = c {
218 t.get_elements().len()
219 } else {
220 0 })
222 })
223 .unwrap_or(0);
224
225 format!(
227 "-> Collecting from LazySet:\n - Filter Function: {:?}\n - From Container: {:?}\n - Progress: Checking element {} / {}\n - Items Collected: {}",
228 self.filter,
231 self.container,
234 self.current_index,
237 container_len,
238 self.collected.len()
240 )
241 }
242}