use crate::ast::{PortType, Value};
use crate::kernel::{SharedCell, SharedCellEntry};
#[derive(Debug, Clone, PartialEq)]
pub enum WriteError {
UnknownWire {
key: String,
},
TypeMismatch {
slot: String,
expected: PortType,
got: PortType,
},
}
impl std::fmt::Display for WriteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WriteError::UnknownWire { key } => {
write!(f, "unknown wire '{key}': no input slot by this name")
}
WriteError::TypeMismatch {
slot,
expected,
got,
} => {
write!(
f,
"type mismatch writing to slot '{slot}': expected {expected:?}, got {got:?} (no auto-adapter available)"
)?;
if matches!(got, PortType::VecF32 | PortType::VecI32)
&& !matches!(
expected,
PortType::VecF32
| PortType::VecI32
| PortType::Str
| PortType::Bytes
| PortType::Json
)
{
write!(
f,
" — collection → scalar requires an explicit \
reduction node in the program (the library \
provides none; `vec_dot` and `vec_norm` are \
the vector reductions that exist)"
)?;
}
Ok(())
}
}
}
}
impl std::error::Error for WriteError {}
pub trait WireKey: sealed::Sealed {
fn resolve<M: Metadata + ?Sized>(self, metadata: &M) -> Option<usize>;
fn describe(&self) -> String;
}
mod sealed {
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for &str {}
impl Sealed for String {}
impl Sealed for &String {}
}
impl WireKey for usize {
#[inline]
fn resolve<M: Metadata + ?Sized>(self, _: &M) -> Option<usize> {
Some(self)
}
#[inline]
fn describe(&self) -> String {
format!("wire[{self}]")
}
}
impl WireKey for &str {
#[inline]
fn resolve<M: Metadata + ?Sized>(self, metadata: &M) -> Option<usize> {
metadata.find_input(self)
}
#[inline]
fn describe(&self) -> String {
(*self).to_string()
}
}
impl WireKey for String {
#[inline]
fn resolve<M: Metadata + ?Sized>(self, metadata: &M) -> Option<usize> {
metadata.find_input(&self)
}
#[inline]
fn describe(&self) -> String {
self.clone()
}
}
impl WireKey for &String {
#[inline]
fn resolve<M: Metadata + ?Sized>(self, metadata: &M) -> Option<usize> {
metadata.find_input(self)
}
#[inline]
fn describe(&self) -> String {
(*self).clone()
}
}
pub trait Metadata {
fn find_input(&self, name: &str) -> Option<usize>;
fn input_names(&self) -> Vec<String>;
fn output_names(&self) -> Vec<String>;
fn coord_count(&self) -> usize;
fn input_port_type(&self, name: &str) -> Option<PortType>;
fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType>;
fn output_port_type(&self, name: &str) -> Option<PortType>;
}
pub trait Dataflow: Metadata {
fn set_wire_idx(&mut self, idx: usize, value: Value) -> Result<(), WriteError>;
fn get_wire_idx(&self, idx: usize) -> Value;
#[inline]
fn set_wire<W: WireKey>(&mut self, key: W, value: Value) -> Result<(), WriteError> {
let key_desc = key.describe();
match key.resolve(self) {
Some(idx) => self.set_wire_idx(idx, value),
None => Err(WriteError::UnknownWire { key: key_desc }),
}
}
#[inline]
fn get_wire<W: WireKey>(&self, key: W) -> Option<Value> {
key.resolve(self).map(|idx| self.get_wire_idx(idx))
}
}
pub trait Construction: Sized {
type Error;
fn root(matter: super::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error>;
fn subscope(&self, matter: super::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error>;
}
pub trait Kernel: Send + internals::KernelInternals {
fn engine(&self) -> crate::compile::select::Engine;
fn set_inputs(&mut self, coords: &[u64]);
fn set_input(&mut self, name: &str, value: Value) -> Result<(), String>;
fn set_cursor(
&mut self,
name: &str,
partition: &crate::iteration::cursor_partition::Partition,
) -> Result<(), String>;
fn eval(&mut self);
fn pull(&mut self, name: &str) -> Value;
fn input_names(&self) -> Vec<String>;
fn output_names(&self) -> Vec<String>;
fn output_type(&self, name: &str) -> Option<PortType>;
fn externs(&self) -> Vec<(String, PortType)>;
fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema];
fn plan(&self) -> crate::EnginePlan;
fn input_value(&self, name: &str) -> Option<Value>;
fn input_index(&self, name: &str) -> Option<usize> {
self.input_names().iter().position(|n| n == name)
}
fn set_input_at(&mut self, index: usize, value: Value) -> Result<(), String> {
let name = self
.input_names()
.get(index)
.cloned()
.ok_or_else(|| format!("no input at index {index}"))?;
self.set_input(&name, value)
}
fn output_index(&self, name: &str) -> Option<usize> {
self.output_names().iter().position(|n| n == name)
}
fn pull_at(&mut self, index: usize) -> Value {
let name = self
.output_names()
.get(index)
.cloned()
.unwrap_or_else(|| panic!("no output at index {index}"));
self.pull(&name)
}
fn traversals(&self) -> &[crate::dsl::traversal::Traversal];
fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String>;
fn traverse_all(&mut self) -> Result<Vec<crate::kernel::TraversalStream>, String> {
(0..self.traversals().len())
.map(|i| self.traverse(i))
.collect()
}
fn invalidate_all(&mut self);
fn shared_cells(&self) -> Vec<SharedCellEntry>;
fn attach_shared_cell(&mut self, name: &str, cell: SharedCell) -> Result<(), String>;
fn into_program(self: Box<Self>) -> std::sync::Arc<dyn KernelProgram>;
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger>;
}
pub(crate) mod internals {
use crate::ast::{PortType, Value};
pub trait KernelInternals {
fn set_traversals(
&mut self,
traversals: Vec<crate::dsl::traversal::Traversal>,
producers: Vec<crate::dsl::traversal::Producer>,
);
fn slot_value(&self, _slot: usize, _ty: PortType) -> Value {
Value::None
}
fn folded_value(&self, name: &str) -> Option<Value>;
fn set_cursor_extent(&mut self, index: usize, extent: u64);
fn reset_to_program(&mut self) {}
}
}
pub trait KernelProgram: Send + Sync {
fn engine(&self) -> crate::compile::select::Engine;
fn create_kernel(self: std::sync::Arc<Self>) -> Box<dyn Kernel>;
fn as_interpreter(
self: std::sync::Arc<Self>,
) -> Option<std::sync::Arc<crate::kernel::PolydatProgram>> {
None
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger>;
}
pub(crate) struct SharedKernel<K>(pub(crate) K);
impl<K: Kernel + Clone + Send + Sync + 'static> KernelProgram for SharedKernel<K> {
fn engine(&self) -> crate::compile::select::Engine {
self.0.engine()
}
fn create_kernel(self: std::sync::Arc<Self>) -> Box<dyn Kernel> {
let mut kernel = self.0.clone();
kernel.reset_to_program();
Box::new(kernel)
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
self.0.ledger()
}
}