sim_lib_sequence/
transducer.rs1use std::sync::Arc;
2
3use sim_kernel::{
4 CORE_SEQUENCE_CLASS_ID, ClassRef, Cx, Error, Object, ObjectCompat, Result, Sequence,
5 SequenceItem, Symbol, Value, seq_next_value,
6};
7
8type ValueMapper = Arc<dyn Fn(&mut Cx, Value) -> Result<Value> + Send + Sync + 'static>;
9type ValuePredicate = Arc<dyn Fn(&mut Cx, &Value) -> Result<bool> + Send + Sync + 'static>;
10type ValueReducer = Arc<dyn Fn(&mut Cx, Value, Value) -> Result<Value> + Send + Sync + 'static>;
11type ValueVisitor = Arc<dyn Fn(&mut Cx, Value) -> Result<()> + Send + Sync + 'static>;
12
13#[derive(Clone)]
15pub enum TransducerStep {
16 Map(ValueMapper),
18 Filter(ValuePredicate),
20}
21
22#[derive(Clone, Default)]
28pub struct TransducerPipeline {
29 steps: Vec<TransducerStep>,
30}
31
32impl TransducerPipeline {
33 pub fn new() -> Self {
35 Self::default()
36 }
37
38 pub fn map(mut self, mapper: ValueMapper) -> Self {
40 self.steps.push(TransducerStep::Map(mapper));
41 self
42 }
43
44 pub fn filter(mut self, predicate: ValuePredicate) -> Self {
46 self.steps.push(TransducerStep::Filter(predicate));
47 self
48 }
49
50 pub fn apply(&self, cx: &mut Cx, mut value: Value) -> Result<Option<Value>> {
55 for step in &self.steps {
56 match step {
57 TransducerStep::Map(mapper) => value = mapper(cx, value)?,
58 TransducerStep::Filter(predicate) => {
59 if !predicate(cx, &value)? {
60 return Ok(None);
61 }
62 }
63 }
64 }
65 Ok(Some(value))
66 }
67}
68
69#[sim_citizen_derive::non_citizen(
70 reason = "transduced sequence adapter; reconstruct from the source sequence and transducer pipeline descriptor",
71 kind = "handle",
72 descriptor = "core/Sequence"
73)]
74pub struct TransducedSequence {
75 source: Value,
76 pipeline: TransducerPipeline,
77}
78
79impl TransducedSequence {
80 pub fn new(source: Value, pipeline: TransducerPipeline) -> Self {
81 Self { source, pipeline }
82 }
83}
84
85impl Object for TransducedSequence {
86 fn display(&self, _cx: &mut Cx) -> Result<String> {
87 Ok("#<transduced-sequence>".to_owned())
88 }
89
90 fn as_any(&self) -> &dyn std::any::Any {
91 self
92 }
93}
94
95impl ObjectCompat for TransducedSequence {
96 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
97 cx.factory().class_stub(
98 CORE_SEQUENCE_CLASS_ID,
99 Symbol::qualified("core", "Sequence"),
100 )
101 }
102
103 fn as_sequence(&self) -> Option<&dyn Sequence> {
104 Some(self)
105 }
106}
107
108impl Sequence for TransducedSequence {
109 fn next_item(&self, cx: &mut Cx) -> Result<Option<SequenceItem>> {
110 while let Some(item) = seq_next_value(cx, &self.source)? {
111 let value = item.into_value(cx)?;
112 if let Some(value) = self.pipeline.apply(cx, value)? {
113 return Ok(Some(SequenceItem::new(value)));
114 }
115 }
116 Ok(None)
117 }
118
119 fn close(&self, cx: &mut Cx) -> Result<()> {
120 if let Some(sequence) = self.source.object().as_sequence() {
121 sequence.close(cx)
122 } else {
123 Ok(())
124 }
125 }
126}
127
128pub fn map_sequence(cx: &mut Cx, source: Value, mapper: ValueMapper) -> Result<Value> {
132 transduced_sequence_value(cx, source, TransducerPipeline::new().map(mapper))
133}
134
135pub fn filter_sequence(cx: &mut Cx, source: Value, predicate: ValuePredicate) -> Result<Value> {
139 transduced_sequence_value(cx, source, TransducerPipeline::new().filter(predicate))
140}
141
142pub fn reduce_sequence(
144 cx: &mut Cx,
145 source: &Value,
146 init: Value,
147 reducer: ValueReducer,
148) -> Result<Value> {
149 transduce(cx, source, TransducerPipeline::new(), init, reducer)
150}
151
152pub fn for_each_sequence(cx: &mut Cx, source: &Value, visitor: ValueVisitor) -> Result<()> {
154 while let Some(item) = seq_next_value(cx, source)? {
155 let value = item.into_value(cx)?;
156 visitor(cx, value)?;
157 }
158 Ok(())
159}
160
161pub fn transduce(
206 cx: &mut Cx,
207 source: &Value,
208 pipeline: TransducerPipeline,
209 mut init: Value,
210 reducer: ValueReducer,
211) -> Result<Value> {
212 while let Some(item) = seq_next_value(cx, source)? {
213 let value = item.into_value(cx)?;
214 if let Some(value) = pipeline.apply(cx, value)? {
215 init = reducer(cx, init, value)?;
216 }
217 }
218 Ok(init)
219}
220
221fn transduced_sequence_value(
222 cx: &mut Cx,
223 source: Value,
224 pipeline: TransducerPipeline,
225) -> Result<Value> {
226 if source.object().as_sequence().is_none() {
227 return Err(Error::TypeMismatch {
228 expected: "sequence",
229 found: "non-sequence",
230 });
231 }
232 cx.factory()
233 .opaque(Arc::new(TransducedSequence::new(source, pipeline)))
234}