use std::sync::Arc;
use sim_kernel::{
CORE_SEQUENCE_CLASS_ID, ClassRef, Cx, Error, Object, ObjectCompat, Result, Sequence,
SequenceItem, Symbol, Value, seq_next_value,
};
type ValueMapper = Arc<dyn Fn(&mut Cx, Value) -> Result<Value> + Send + Sync + 'static>;
type ValuePredicate = Arc<dyn Fn(&mut Cx, &Value) -> Result<bool> + Send + Sync + 'static>;
type ValueReducer = Arc<dyn Fn(&mut Cx, Value, Value) -> Result<Value> + Send + Sync + 'static>;
type ValueVisitor = Arc<dyn Fn(&mut Cx, Value) -> Result<()> + Send + Sync + 'static>;
#[derive(Clone)]
pub enum TransducerStep {
Map(ValueMapper),
Filter(ValuePredicate),
}
#[derive(Clone, Default)]
pub struct TransducerPipeline {
steps: Vec<TransducerStep>,
}
impl TransducerPipeline {
pub fn new() -> Self {
Self::default()
}
pub fn map(mut self, mapper: ValueMapper) -> Self {
self.steps.push(TransducerStep::Map(mapper));
self
}
pub fn filter(mut self, predicate: ValuePredicate) -> Self {
self.steps.push(TransducerStep::Filter(predicate));
self
}
pub fn apply(&self, cx: &mut Cx, mut value: Value) -> Result<Option<Value>> {
for step in &self.steps {
match step {
TransducerStep::Map(mapper) => value = mapper(cx, value)?,
TransducerStep::Filter(predicate) => {
if !predicate(cx, &value)? {
return Ok(None);
}
}
}
}
Ok(Some(value))
}
}
#[sim_citizen_derive::non_citizen(
reason = "transduced sequence adapter; reconstruct from the source sequence and transducer pipeline descriptor",
kind = "handle",
descriptor = "core/Sequence"
)]
pub struct TransducedSequence {
source: Value,
pipeline: TransducerPipeline,
}
impl TransducedSequence {
pub fn new(source: Value, pipeline: TransducerPipeline) -> Self {
Self { source, pipeline }
}
}
impl Object for TransducedSequence {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<transduced-sequence>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for TransducedSequence {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.factory().class_stub(
CORE_SEQUENCE_CLASS_ID,
Symbol::qualified("core", "Sequence"),
)
}
fn as_sequence(&self) -> Option<&dyn Sequence> {
Some(self)
}
}
impl Sequence for TransducedSequence {
fn next_item(&self, cx: &mut Cx) -> Result<Option<SequenceItem>> {
while let Some(item) = seq_next_value(cx, &self.source)? {
let value = item.into_value(cx)?;
if let Some(value) = self.pipeline.apply(cx, value)? {
return Ok(Some(SequenceItem::new(value)));
}
}
Ok(None)
}
fn close(&self, cx: &mut Cx) -> Result<()> {
if let Some(sequence) = self.source.object().as_sequence() {
sequence.close(cx)
} else {
Ok(())
}
}
}
pub fn map_sequence(cx: &mut Cx, source: Value, mapper: ValueMapper) -> Result<Value> {
transduced_sequence_value(cx, source, TransducerPipeline::new().map(mapper))
}
pub fn filter_sequence(cx: &mut Cx, source: Value, predicate: ValuePredicate) -> Result<Value> {
transduced_sequence_value(cx, source, TransducerPipeline::new().filter(predicate))
}
pub fn reduce_sequence(
cx: &mut Cx,
source: &Value,
init: Value,
reducer: ValueReducer,
) -> Result<Value> {
transduce(cx, source, TransducerPipeline::new(), init, reducer)
}
pub fn for_each_sequence(cx: &mut Cx, source: &Value, visitor: ValueVisitor) -> Result<()> {
while let Some(item) = seq_next_value(cx, source)? {
let value = item.into_value(cx)?;
visitor(cx, value)?;
}
Ok(())
}
pub fn transduce(
cx: &mut Cx,
source: &Value,
pipeline: TransducerPipeline,
mut init: Value,
reducer: ValueReducer,
) -> Result<Value> {
while let Some(item) = seq_next_value(cx, source)? {
let value = item.into_value(cx)?;
if let Some(value) = pipeline.apply(cx, value)? {
init = reducer(cx, init, value)?;
}
}
Ok(init)
}
fn transduced_sequence_value(
cx: &mut Cx,
source: Value,
pipeline: TransducerPipeline,
) -> Result<Value> {
if source.object().as_sequence().is_none() {
return Err(Error::TypeMismatch {
expected: "sequence",
found: "non-sequence",
});
}
cx.factory()
.opaque(Arc::new(TransducedSequence::new(source, pipeline)))
}