1use crate::ast::{NodeMeta, PolydatNode, Port, PortType, Slot, Value};
16
17#[derive(Debug, Clone, PartialEq)]
19pub struct ConvertError {
20 pub from: PortType,
22 pub to: PortType,
24 pub reason: String,
26}
27
28impl std::fmt::Display for ConvertError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(
31 f,
32 "cannot convert {} to {}: {}",
33 self.from, self.to, self.reason
34 )
35 }
36}
37
38impl std::error::Error for ConvertError {}
39
40pub fn to_port(value: Value, to: PortType) -> Result<Value, ConvertError> {
48 if to == PortType::Dyn || value.satisfies_slot(to) {
49 return Ok(value);
50 }
51 let from = value.port_type();
52 let Some(adapter) = crate::compile::assembly::boundary_adapter(from, to) else {
53 return Err(ConvertError {
54 from,
55 to,
56 reason: "no conversion between these types".into(),
57 });
58 };
59 let capture = crate::kernel::engines::EvalPanicCaptureGuard::arm();
62 let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
63 let mut outputs = vec![Value::None];
64 adapter.eval(&[value], &mut outputs);
65 outputs.remove(0)
66 }));
67 drop(capture);
68 match outcome {
69 Ok(converted) => Ok(converted),
70 Err(payload) => Err(ConvertError {
71 from,
72 to,
73 reason: crate::kernel::panic_payload_text(payload.as_ref()),
74 }),
75 }
76}
77
78pub(crate) struct InputConverter {
82 meta: NodeMeta,
83 input: String,
84 to: PortType,
85}
86
87impl InputConverter {
88 pub(crate) fn new(input: &str, to: PortType) -> Self {
90 Self {
91 meta: NodeMeta {
92 name: converter_name(input, to),
93 outs: vec![Port::new("output", to)],
94 ins: vec![Slot::Wire(Port::new("input", PortType::Dyn))],
95 },
96 input: input.to_string(),
97 to,
98 }
99 }
100}
101
102pub(crate) fn converter_name(input: &str, to: PortType) -> String {
104 format!("__convert_{input}_{}", to.to_keyword())
105}
106
107fn convert_input(input: &str, value: Value, to: PortType) -> Value {
109 match to_port(value, to) {
110 Ok(v) => v,
111 Err(e) => panic!("input '{input}': {e}"),
112 }
113}
114
115impl PolydatNode for InputConverter {
116 fn meta(&self) -> &NodeMeta {
117 &self.meta
118 }
119
120 fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
121 outputs[0] = convert_input(&self.input, inputs[0].clone(), self.to);
122 }
123
124 fn compiled_slot(
129 &self,
130 _wire_types: &[PortType],
131 _engine: crate::compile::select::Engine,
132 ) -> Option<crate::ast::CompiledSlotKit> {
133 use crate::ast::SlotShape;
134 let (input, to) = (self.input.clone(), self.to);
135 Some(crate::ast::CompiledSlotKit {
136 scratch: to.scratch_elem().into_iter().collect(),
137 op: Box::new(
138 move |inputs: &[u64],
139 outputs: &mut [u64],
140 scratch: &mut [crate::ast::ScratchBuf]| {
141 let value = crate::derive_support::read_poly(PortType::Dyn, inputs);
142 let converted = convert_input(&input, value, to);
143 crate::derive_support::write_poly(to, converted, scratch, outputs);
144 },
145 ),
146 })
147 }
148}