Skip to main content

somatize_core/
node.rs

1//! The contract for what a node executes. Just the one.
2//!
3//! A node is a function: a [`Value`] in, a [`Value`] out. There is no second
4//! kind and no second shape — a filter and a step are one type, and the
5//! two-variant return value that once carried the distinction turned out not to
6//! be needed either.
7//!
8//! A node that needs something from the world — a model, a tool, an index —
9//! **calls it**, holding whatever client that takes. What is kept for whoever
10//! wants something injected instead is the **channel**: [`Ctx`] is where the
11//! executor hands a node what it knows, and adding to it changes no signature.
12
13use crate::{Device, Value};
14
15/// Something a node knows how to do. `Send + Sync` because a Python `Graph` is
16/// a pyclass — which PyO3 requires to be `Send` — and it carries the catalog.
17pub trait Node: Send + Sync {
18    /// Runs it. `input` is what arrived along the edges. It runs to the end:
19    /// whatever it takes happens inside, and the engine neither counts nor
20    /// bounds it.
21    fn forward(&self, input: &Value, ctx: &Ctx<'_>) -> Result<Value, NodeError>;
22}
23
24/// What a node knows beyond its input, which travels separately. A type rather
25/// than an argument because it is the **channel**: adding to it is additive, and
26/// every node ever written has this signature.
27#[derive(Debug, Clone, Copy)]
28pub struct Ctx<'a> {
29    /// Where this node was said to run, if it was said. It arrives as
30    /// **information**: the core cannot move anything to a GPU, so the one that
31    /// obeys is the node.
32    pub device: Option<&'a Device>,
33}
34
35/// What a node can answer when it cannot advance.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct NodeError(String);
38
39impl NodeError {
40    /// A failure described by a message.
41    pub fn new(message: impl Into<String>) -> Self {
42        Self(message.into())
43    }
44
45    /// The message.
46    pub fn message(&self) -> &str {
47        &self.0
48    }
49}
50
51impl std::fmt::Display for NodeError {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        f.write_str(&self.0)
54    }
55}
56
57impl std::error::Error for NodeError {}