onion_vm/types/
lazy_set.rs

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                // Keep the collector alive until after we use its weak reference
108                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                                // 如果是布尔值 true,表示需要收集当前元素
142                                if let Some(item) = tuple.get_elements().get(self.current_index - 1)
143                                {
144                                    self.collected.push(item.stabilize());
145                                    Ok(())
146                                } else {
147                                    // 所有元素都处理完了
148                                    Ok(())
149                                }
150                            }
151                            _ => Err(RuntimeError::DetailedError(
152                                "Container must be a tuple".into(),
153                            )),
154                        }
155                    }
156                    _ => {
157                        // 如果返回的不是布尔值,直接忽略
158                        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                        // 使用索引获取当前元素
176                        if let Some(item) = tuple.get_elements().get(self.current_index) {
177                            self.current_index += 1; // 移动到下一个元素
178                            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                            // 所有元素都处理完了
199                            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        // 尝试获取容器的总长度,用于进度报告
213        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 // 如果容器不是元组或弱引用失效,返回0
221                })
222            })
223            .unwrap_or(0);
224
225        // 使用 format! 宏构建一个清晰、多行的字符串
226        format!(
227            "-> Collecting from LazySet:\n   - Filter Function: {:?}\n   - From Container: {:?}\n   - Progress: Checking element {} / {}\n   - Items Collected: {}",
228            // 1. 过滤器信息
229            // 使用 Debug 格式打印 filter 对象,以识别是哪个 lambda
230            self.filter,
231            // 2. 容器信息
232            // 使用 Debug 格式打印 container 对象
233            self.container,
234            // 3. 进度信息
235            // current_index 告诉我们下一个要检查的元素索引
236            self.current_index,
237            container_len,
238            // 4. 已收集结果的数量
239            self.collected.len()
240        )
241    }
242}