ijima_core/error.rs
1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Centralized error type for the Ijima memory backend.
5
6use thiserror::Error;
7
8/// The single error enum returned by every fallible Ijima operation.
9///
10/// Variants follow the IA convention of carrying structured context
11/// rather than opaque strings, so callers can react to specific failure
12/// modes (a duplicate write, a missing entity, an unrunnable miner).
13#[derive(Debug, Error)]
14pub enum IjimaError {
15 /// A memory write was rejected because equivalent content already
16 /// exists in the palace (content-hash or semantic dedup hit).
17 #[error("duplicate memory rejected: {detail}")]
18 Duplicate {
19 /// Human-readable description of what collided.
20 detail: String,
21 },
22
23 /// A referenced entity, memory, or session was not found.
24 #[error("not found: {detail}")]
25 NotFound {
26 /// What was missing.
27 detail: String,
28 },
29
30 /// The backing store returned an error (SQLite, vector index, file I/O).
31 #[error("store error: {detail}")]
32 Store {
33 /// Underlying store failure description.
34 detail: String,
35 },
36
37 /// A harness-supplied identifier or payload failed validation.
38 #[error("invalid input: {detail}")]
39 InvalidInput {
40 /// Why the input was rejected.
41 detail: String,
42 },
43
44 /// The schema migration/import path failed.
45 #[error("schema error: {detail}")]
46 Schema {
47 /// Migration/import failure description.
48 detail: String,
49 },
50
51 /// The mining engine could not complete an extraction pass.
52 #[error("mining error: {detail}")]
53 Mining {
54 /// Extraction failure description.
55 detail: String,
56 },
57
58 /// A transport-layer failure (HTTP client/server, serialization).
59 #[error("transport error: {detail}")]
60 Transport {
61 /// Transport failure description.
62 detail: String,
63 },
64}
65
66impl IjimaError {
67 /// Construct a [`Duplicate`] error with the given detail string.
68 pub fn duplicate(detail: impl Into<String>) -> Self {
69 Self::Duplicate {
70 detail: detail.into(),
71 }
72 }
73
74 /// Construct a [`NotFound`] error with the given detail string.
75 pub fn not_found(detail: impl Into<String>) -> Self {
76 Self::NotFound {
77 detail: detail.into(),
78 }
79 }
80
81 /// Construct an [`InvalidInput`] error with the given detail string.
82 pub fn invalid_input(detail: impl Into<String>) -> Self {
83 Self::InvalidInput {
84 detail: detail.into(),
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn duplicate_error_carries_detail() {
95 let err = IjimaError::duplicate("content hash abc123 already present");
96 match err {
97 IjimaError::Duplicate { detail } => {
98 assert_eq!(detail, "content hash abc123 already present");
99 }
100 other => panic!("expected Duplicate, got {other:?}"),
101 }
102 }
103
104 #[test]
105 fn error_display_is_human_readable() {
106 let err = IjimaError::not_found("session sess_42");
107 assert_eq!(err.to_string(), "not found: session sess_42");
108 }
109}