use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{Interpreter, DynamicFnInfo};
use mumu::parser::interpreter::apply_n_ary_function_value;
use mumu::parser::types::{Value, FunctionValue, InkIteratorKind};
pub fn register_flow_to_array(interp: &mut Interpreter) {
let f = Arc::new(Mutex::new(flow_to_array_bridge_fn));
let info = DynamicFnInfo::new(f, true);
interp.register_dynamic_function_ex("flow:to_array", info);
interp.set_variable(
"flow:to_array",
Value::Function(Box::new(FunctionValue::Named("flow:to_array".to_string())))
);
}
fn flow_to_array_bridge_fn(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 1 {
return Err(format!("flow:to_array => expected exactly 1 argument, got {}", args.len()));
}
let source_val = args.remove(0);
match source_val {
Value::InkIterator(handle) => {
let mut output_values = Vec::new();
match &handle.kind {
InkIteratorKind::Core(state_arc) => {
loop {
let item_res = {
let mut guard = state_arc.lock().map_err(|_| "flow:to_array => InkIterator lock error".to_string())?;
if guard.done || guard.current >= guard.end {
None
} else {
let item = guard.current;
guard.current += 1;
Some(Value::Int(item as i32))
}
};
match item_res {
Some(v) => output_values.push(v),
None => break,
}
}
coerce_to_single_array(output_values)
}
InkIteratorKind::Plugin(plugin_arc) => {
let mut plugin = plugin_arc.lock().map_err(|_| "flow:to_array => plugin lock error".to_string())?;
loop {
match plugin.next_value() {
Ok(val) => output_values.push(val),
Err(e) if e == "NO_MORE_DATA" => break,
Err(e) => return Err(e),
}
}
coerce_to_single_array(output_values)
}
}
}
Value::InkTransform(fb) => {
let mut output_values = Vec::new();
loop {
match apply_n_ary_function_value(interp, fb.clone(), vec![]) {
Ok(next_val) => output_values.push(next_val),
Err(e) => {
if e == "NO_MORE_DATA" {
break;
} else {
return Err(e);
}
}
}
}
coerce_to_single_array(output_values)
}
other => Err(format!("flow:to_array => argument must be InkIterator or InkTransform, got {:?}", other)),
}
}
fn coerce_to_single_array(items: Vec<Value>) -> Result<Value, String> {
if items.is_empty() {
return Ok(Value::IntArray(vec![]));
}
let all_int = items.iter().all(|v| matches!(v, Value::Int(_)));
if all_int {
let mut temp = Vec::with_capacity(items.len());
for v in items {
if let Value::Int(i) = v {
temp.push(i);
}
}
return Ok(Value::IntArray(temp));
}
let all_float = items.iter().all(|v| matches!(v, Value::Float(_)));
if all_float {
let mut temp = Vec::with_capacity(items.len());
for v in items {
if let Value::Float(f) = v {
temp.push(f);
}
}
return Ok(Value::FloatArray(temp));
}
let all_str = items.iter().all(|v| matches!(v, Value::SingleString(_)));
if all_str {
let mut temp = Vec::with_capacity(items.len());
for v in items {
if let Value::SingleString(s) = v {
temp.push(s);
}
}
return Ok(Value::StrArray(temp));
}
Err("flow:to_array => array has mixed/unhandled types; cannot unify".to_string())
}