use crate::ast::{NodeMeta, PolydatNode, Port, PortType, Slot, Value};
#[derive(Debug, Clone, PartialEq)]
pub struct ConvertError {
pub from: PortType,
pub to: PortType,
pub reason: String,
}
impl std::fmt::Display for ConvertError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"cannot convert {} to {}: {}",
self.from, self.to, self.reason
)
}
}
impl std::error::Error for ConvertError {}
pub fn to_port(value: Value, to: PortType) -> Result<Value, ConvertError> {
if to == PortType::Dyn || value.satisfies_slot(to) {
return Ok(value);
}
let from = value.port_type();
let Some(adapter) = crate::compile::assembly::boundary_adapter(from, to) else {
return Err(ConvertError {
from,
to,
reason: "no conversion between these types".into(),
});
};
let capture = crate::kernel::engines::EvalPanicCaptureGuard::arm();
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut outputs = vec![Value::None];
adapter.eval(&[value], &mut outputs);
outputs.remove(0)
}));
drop(capture);
match outcome {
Ok(converted) => Ok(converted),
Err(payload) => Err(ConvertError {
from,
to,
reason: crate::kernel::panic_payload_text(payload.as_ref()),
}),
}
}
pub(crate) struct InputConverter {
meta: NodeMeta,
input: String,
to: PortType,
}
impl InputConverter {
pub(crate) fn new(input: &str, to: PortType) -> Self {
Self {
meta: NodeMeta {
name: converter_name(input, to),
outs: vec![Port::new("output", to)],
ins: vec![Slot::Wire(Port::new("input", PortType::Dyn))],
},
input: input.to_string(),
to,
}
}
}
pub(crate) fn converter_name(input: &str, to: PortType) -> String {
format!("__convert_{input}_{}", to.to_keyword())
}
fn convert_input(input: &str, value: Value, to: PortType) -> Value {
match to_port(value, to) {
Ok(v) => v,
Err(e) => panic!("input '{input}': {e}"),
}
}
impl PolydatNode for InputConverter {
fn meta(&self) -> &NodeMeta {
&self.meta
}
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
outputs[0] = convert_input(&self.input, inputs[0].clone(), self.to);
}
fn compiled_slot(
&self,
_wire_types: &[PortType],
_engine: crate::compile::select::Engine,
) -> Option<crate::ast::CompiledSlotKit> {
use crate::ast::SlotShape;
let (input, to) = (self.input.clone(), self.to);
Some(crate::ast::CompiledSlotKit {
scratch: to.scratch_elem().into_iter().collect(),
op: Box::new(
move |inputs: &[u64],
outputs: &mut [u64],
scratch: &mut [crate::ast::ScratchBuf]| {
let value = crate::derive_support::read_poly(PortType::Dyn, inputs);
let converted = convert_input(&input, value, to);
crate::derive_support::write_poly(to, converted, scratch, outputs);
},
),
})
}
}