use thiserror::Error;
use crate::node::{NodeId, PortKind};
#[derive(Debug, Error, Clone)]
pub enum LumenError {
#[error("graph validation error: {0}")]
GraphValidation(#[from] GraphValidationError),
#[error("property resolution error: {0}")]
Property(#[from] PropertyError),
#[error("expression error: {0}")]
Expression(#[from] ExpressionError),
#[error("media error: {0}")]
Media(#[from] MediaError),
#[error("render error: {0}")]
Render(#[from] RenderError),
}
#[derive(Debug, Error, Clone)]
pub enum GraphValidationError {
#[error("graph contains a cycle")]
Cycle { path: Vec<NodeId> },
#[error("connection target node is missing")]
MissingTargetNode { node_id: NodeId },
#[error("connection source node is missing")]
MissingSourceNode { node_id: NodeId },
#[error("connection target port `{port}` is missing")]
MissingTargetPort { node_id: NodeId, port: String },
#[error("connection source port `{port}` is missing")]
MissingSourcePort { node_id: NodeId, port: String },
#[error("missing required input `{port}`")]
MissingRequiredInput { node_id: NodeId, port: String },
#[error("port kind mismatch for `{to_port}`")]
PortKindMismatch {
from_node: NodeId,
from_port: String,
from_kind: PortKind,
to_node: NodeId,
to_port: String,
expected_kind: PortKind,
},
#[error("exactly one media output node is required")]
MissingMediaOutput,
#[error("exactly one media output node is required")]
MultipleMediaOutputs { count: usize },
#[error("target node is not reachable or missing")]
InvalidEvaluationTarget { node_id: NodeId },
}
#[derive(Debug, Error, Clone)]
pub enum PropertyError {
#[error("property `{property_path}` is missing")]
MissingProperty {
node_id: NodeId,
property_path: String,
},
#[error("property `{property_path}` has invalid type")]
InvalidType {
node_id: NodeId,
property_path: String,
expected: &'static str,
actual: &'static str,
},
}
#[derive(Debug, Error, Clone)]
pub enum ExpressionError {
#[error("expression parse failed: {details}")]
Parse {
path: Option<String>,
details: String,
},
#[error("expression evaluation failed: {details}")]
Evaluate {
path: Option<String>,
details: String,
},
#[error("undefined variable in expression")]
UndefinedVariable { path: Option<String>, name: String },
}
#[derive(Debug, Error, Clone)]
pub enum MediaError {
#[error("media source not found: {media_source}")]
SourceNotFound { media_source: String },
#[error("media decoding failed for {media_source}: {details}")]
Decode {
media_source: String,
details: String,
},
#[error("media frame {frame} is out of range for {media_source} ({frame_count} frames)")]
FrameOutOfRange {
media_source: String,
frame: u32,
frame_count: u32,
},
}
#[derive(Debug, Error, Clone)]
pub enum RenderError {
#[error("frame is out of range")]
FrameOutOfRange { frame: u32, duration_frames: u32 },
#[error("node evaluation failed")]
NodeEvaluation {
frame: u32,
node_id: NodeId,
node_kind: &'static str,
details: String,
},
#[error("referenced node is missing from the graph")]
MissingNode { frame: u32, node_id: NodeId },
#[error("surface allocation failed")]
SurfaceAllocation { width: u32, height: u32 },
#[error("node output is missing")]
MissingNodeOutput { frame: u32, node_id: NodeId },
#[error("node output has invalid type")]
InvalidNodeOutputType {
frame: u32,
node_id: NodeId,
expected: &'static str,
actual: &'static str,
},
#[error("media output node did not produce raster output")]
InvalidMediaOutputType { frame: u32, node_id: NodeId },
#[error("pixel readback failed")]
PixelReadbackFailed { width: u32, height: u32 },
#[error("all scratch surfaces are currently in use")]
ScratchSurfaceUnavailable,
#[error("{backend} backend is busy")]
BackendBusy { backend: &'static str },
#[error("render cancelled")]
Cancelled { frame: u32 },
#[error("GPU render failed: {details}")]
Gpu { details: String },
}
#[derive(Debug, Clone, PartialEq)]
pub enum Warning {
FpsMismatch {
node_id: NodeId,
composition_fps: f32,
source_fps: f32,
},
CapabilityMissing {
node_id: NodeId,
requirement: &'static str,
},
}