limen-core 0.1.0-alpha.1

Limen core contracts and primitives.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
//! Error families used across Limen Core.
//!
//! Errors are designed to stay lightweight and `no_std` friendly by default.
//! Implementations of `std::error::Error` are only provided when the `std`
//! feature is enabled.

use core::fmt;

#[cfg(feature = "std")]
use std::error::Error;

use crate::types::EdgeIndex;

// **** Edge Errors *****

/// Errors originating from queue operations.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueueError {
    /// The queue is at or above the hard watermark capacity.
    AtOrAboveHardCap,
    /// The queue is backpressured but not full; caller may retry later.
    Backpressured,
    /// The queue is empty when a pop operation was requested.
    Empty,
    /// The operation is not supported by this queue/backend (e.g., reference peek in concurrent mode).
    Unsupported,
    /// The queue lock has been poisoned (concurrent mode only).
    Poisoned,
}

impl fmt::Display for QueueError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            QueueError::AtOrAboveHardCap => {
                f.write_str("queue is at or above the hard watermark capacity")
            }
            QueueError::Backpressured => {
                f.write_str("queue is backpressured but not full; caller may retry later")
            }
            QueueError::Empty => f.write_str("queue is empty"),
            QueueError::Unsupported => f.write_str("operation unsupported by this queue/backend"),
            QueueError::Poisoned => f.write_str("queue lock is poisoned"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for QueueError {}

// ***** Node Errors *****

/// Errors from node execution.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NodeErrorKind {
    /// Inputs were not available to progress this node.
    NoInput,
    /// Outputs could not be enqueued due to backpressure.
    Backpressured,
    /// An execution budget or deadline was exceeded.
    OverBudget,
    /// External dependency (device, transport) was unavailable or timed out.
    ExternalUnavailable,
    /// A generic failure in node logic.
    ExecutionFailed,
}

impl fmt::Display for NodeErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            NodeErrorKind::NoInput => {
                f.write_str("inputs were not available to progress this node")
            }
            NodeErrorKind::Backpressured => {
                f.write_str("outputs could not be enqueued due to backpressure")
            }
            NodeErrorKind::OverBudget => {
                f.write_str("an execution budget or deadline was exceeded")
            }
            NodeErrorKind::ExternalUnavailable => {
                f.write_str("an external dependency was unavailable or timed out")
            }
            NodeErrorKind::ExecutionFailed => {
                f.write_str("a generic failure occurred in node logic")
            }
        }
    }
}

/// A unified error used by node lifecycle methods.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NodeError {
    /// The error kind.
    kind: NodeErrorKind,
    /// Optional numeric code for platform/backend-specific mapping.
    code: u32,
}

impl NodeError {
    /// Construct a new node error with the given kind and optional code.
    pub const fn new(kind: NodeErrorKind, code: u32) -> Self {
        Self { kind, code }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "node error: {} (code: {})", self.kind, self.code)
    }
}

#[cfg(feature = "std")]
impl Error for NodeError {}

impl NodeError {
    /// Creates a `NoInput` error.
    #[inline]
    pub const fn no_input() -> Self {
        Self::new(NodeErrorKind::NoInput, 0)
    }
    /// Creates a `Backpressured` error.
    #[inline]
    pub const fn backpressured() -> Self {
        Self::new(NodeErrorKind::Backpressured, 0)
    }
    /// Creates an `OverBudget` error.
    #[inline]
    pub const fn over_budget() -> Self {
        Self::new(NodeErrorKind::OverBudget, 0)
    }
    /// Creates an `ExternalUnavailable` error.
    #[inline]
    pub const fn external_unavailable() -> Self {
        Self::new(NodeErrorKind::ExternalUnavailable, 0)
    }
    /// Creates an `ExecutionFailed` error.
    #[inline]
    pub const fn execution_failed() -> Self {
        Self::new(NodeErrorKind::ExecutionFailed, 0)
    }

    /// Same as the above but lets you tack on a backend/platform error code.
    #[inline]
    pub const fn with_code(mut self, code: u32) -> Self {
        self.code = code;
        self
    }

    /// Return the error kind.
    #[inline]
    pub const fn kind(&self) -> &NodeErrorKind {
        &self.kind
    }

    /// Return the numeric code associated with this error.
    #[inline]
    pub const fn code(&self) -> &u32 {
        &self.code
    }
}

impl From<NodeErrorKind> for NodeError {
    #[inline]
    fn from(kind: NodeErrorKind) -> Self {
        NodeError::new(kind, 0)
    }
}

/// Errors related to model loading and inference execution.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InferenceErrorKind {
    /// A model artifact is invalid or unsupported.
    InvalidArtifact,
    /// The input or output payload is incompatible with the model.
    ShapeOrTypeMismatch,
    /// Execution failed inside the backend.
    ExecutionFailed,
    /// Backend resource not available (e.g., device).
    ResourceUnavailable,
}

impl fmt::Display for InferenceErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            InferenceErrorKind::InvalidArtifact => {
                f.write_str("model artifact is invalid or unsupported")
            }
            InferenceErrorKind::ShapeOrTypeMismatch => {
                f.write_str("input or output payload is incompatible with the model")
            }
            InferenceErrorKind::ExecutionFailed => {
                f.write_str("execution failed inside the backend")
            }
            InferenceErrorKind::ResourceUnavailable => {
                f.write_str("backend resource is unavailable")
            }
        }
    }
}

/// Inference error including a kind and optional code.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InferenceError {
    /// Error kind.
    kind: InferenceErrorKind,
    /// Optional numeric code.
    code: u32,
}

impl InferenceError {
    /// Construct a new inference error.
    pub const fn new(kind: InferenceErrorKind, code: u32) -> Self {
        Self { kind, code }
    }

    /// Return the inference error kind.
    #[inline]
    pub const fn kind(&self) -> &InferenceErrorKind {
        &self.kind
    }

    /// Return the numeric code associated with this inference error.
    #[inline]
    pub const fn code(&self) -> &u32 {
        &self.code
    }
}

impl fmt::Display for InferenceError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "inference error: {} (code: {})", self.kind, self.code)
    }
}

#[cfg(feature = "std")]
impl Error for InferenceError {}

// ***** Graph Errors *****

/// Graph validation and wiring errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphError {
    /// The graph contains a cycle.
    // TODO: ENABLE?
    Cyclic,
    /// Port schema or memory placement is incompatible across an edge.
    IncompatiblePorts,
    /// Queue capacity or watermark configuration is invalid.
    InvalidCapacity,
    /// Invalid graph index used.
    InvalidEdgeIndex,
    /// Failed to sample occupancy for the given edge (e.g., poisoned lock or device error).
    OccupancySampleFailed(EdgeIndex),
}

impl fmt::Display for GraphError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            GraphError::Cyclic => f.write_str("graph contains a cycle"),
            GraphError::IncompatiblePorts => {
                f.write_str("port schema or memory placement is incompatible across an edge")
            }
            GraphError::InvalidCapacity => {
                f.write_str("queue capacity or watermark configuration is invalid")
            }
            GraphError::InvalidEdgeIndex => f.write_str("edge index is invalid"),
            GraphError::OccupancySampleFailed(ei) => {
                write!(f, "failed to sample occupancy for edge {}", ei.as_usize())
            }
        }
    }
}

#[cfg(feature = "std")]
impl Error for GraphError {}

// ***** Runtime Errors *****

/// Generic runtime error kinds.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeErrorKind {
    /// An invariant has been violated (e.g., cyclic graph or type mismatch).
    InvariantViolation,
    /// A platform service was requested but is unavailable.
    PlatformUnavailable,
    /// The operation is unsupported in this profile or configuration.
    Unsupported,
    /// An unspecified failure occurred.
    Unknown,
}

impl fmt::Display for RuntimeErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RuntimeErrorKind::InvariantViolation => f.write_str("an invariant has been violated"),
            RuntimeErrorKind::PlatformUnavailable => {
                f.write_str("a requested platform service is unavailable")
            }
            RuntimeErrorKind::Unsupported => {
                f.write_str("the operation is unsupported in this profile or configuration")
            }
            RuntimeErrorKind::Unknown => f.write_str("an unspecified failure occurred"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for RuntimeErrorKind {}

/// Scheduler-related errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SchedulerError {
    /// The scheduler cannot proceed due to an invariant violation.
    InvariantViolation,
    /// An internal error occurred.
    Internal,
}

impl fmt::Display for SchedulerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SchedulerError::InvariantViolation => {
                f.write_str("the scheduler cannot proceed due to an invariant violation")
            }
            SchedulerError::Internal => f.write_str("an internal scheduler error occurred"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for SchedulerError {}

// ***** Source Errors *****

/// Source / sensor related errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SensorError {
    /// Sensor open failed.
    OpenFailed,
    /// Sensor read failed.
    ReadFailed,
    /// Sensor stream ended.
    EndOfStream,
    /// Sensor reset failed.
    ResetFailed,
    /// Invalid sensor configuration
    ConfigurationInvalid,
}

impl fmt::Display for SensorError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SensorError::OpenFailed => f.write_str("sensor open failed"),
            SensorError::ReadFailed => f.write_str("sensor read failed"),
            SensorError::EndOfStream => f.write_str("sensor stream ended"),
            SensorError::ResetFailed => f.write_str("sensor reset failed"),
            SensorError::ConfigurationInvalid => f.write_str("invalid sensor configuration"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for SensorError {}

// ***** Sink Errors *****

/// Output / sink related errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputError {
    /// Sink write failed.
    WriteFailed,
    /// Sink flush failed.
    FlushFailed,
}

impl fmt::Display for OutputError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OutputError::WriteFailed => f.write_str("sink write failed"),
            OutputError::FlushFailed => f.write_str("sink flush failed"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for OutputError {}

// ***** Memory Errors *****

/// Errors originating from memory manager operations.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryError {
    /// No free slots available in the manager.
    NoFreeSlots,
    /// Token index is out of range (invalid token) or the slot is not allocated.
    BadToken,
    /// Attempted to free a slot that is not currently allocated.
    NotAllocated,
    /// Attempted to borrow (read or write) but slot is already borrowed
    /// in an incompatible way.
    AlreadyBorrowed,
    /// Attempted to free a slot while borrows are still active.
    BorrowActive,
    /// A synchronization primitive (lock) was poisoned.
    Poisoned,
}

impl fmt::Display for MemoryError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MemoryError::NoFreeSlots => f.write_str("no free slots in memory manager"),
            MemoryError::BadToken => f.write_str("token index out of range or slot not allocated"),
            MemoryError::NotAllocated => {
                f.write_str("attempted to free a slot that is not allocated")
            }
            MemoryError::AlreadyBorrowed => f.write_str("slot already borrowed incompatibly"),
            MemoryError::BorrowActive => f.write_str("cannot free slot while borrows are active"),
            MemoryError::Poisoned => f.write_str("synchronization primitive is poisoned"),
        }
    }
}

#[cfg(feature = "std")]
impl Error for MemoryError {}

// ***** Runtime Invariant Errors *****

/// Runtime invariants that were violated (programmer errors).
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeInvariantError {
    /// `step` was called before `init` installed a clock.
    UninitializedClock,
    /// `step` was called before `init` installed telemetry.
    UninitializedTelemetry,
}

impl fmt::Display for RuntimeInvariantError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RuntimeInvariantError::UninitializedClock => f.write_str("clock not present"),
            RuntimeInvariantError::UninitializedTelemetry => f.write_str("telemetry not present"),
        }
    }
}

/// Error surface for runtimes: can wrap graph- and node-level errors.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeError {
    /// Graph errors
    Graph(GraphError),
    /// Node errors
    Node(NodeError),
    /// Internal runtime invariants that were violated.
    RuntimeInvariant(RuntimeInvariantError),
}

impl From<GraphError> for RuntimeError {
    #[inline]
    fn from(e: GraphError) -> Self {
        RuntimeError::Graph(e)
    }
}

impl From<NodeError> for RuntimeError {
    #[inline]
    fn from(e: NodeError) -> Self {
        RuntimeError::Node(e)
    }
}

impl From<RuntimeInvariantError> for RuntimeError {
    #[inline]
    fn from(e: RuntimeInvariantError) -> Self {
        RuntimeError::RuntimeInvariant(e)
    }
}

impl fmt::Display for RuntimeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RuntimeError::Graph(e) => write!(f, "runtime graph error: {e}"),
            RuntimeError::Node(e) => write!(f, "runtime node error: {e}"),
            RuntimeError::RuntimeInvariant(e) => write!(f, "runtime invariant error: {e}"),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for RuntimeError {}