1use std::fmt;
4
5use thiserror::Error;
6
7pub type RuntimeResult<T> = Result<T, RuntimeError>;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct MissingPackDependency {
12 pub from: String,
13 pub requires: String,
14}
15
16impl fmt::Display for MissingPackDependency {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 write!(
19 f,
20 "pack '{}' requires '{}', but '{}' is not in the loaded pack set",
21 self.from, self.requires, self.requires
22 )
23 }
24}
25
26impl std::error::Error for MissingPackDependency {}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct MissingPackDependencies {
31 pub missing: Vec<MissingPackDependency>,
32}
33
34impl fmt::Display for MissingPackDependencies {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 let parts: Vec<String> = self.missing.iter().map(ToString::to_string).collect();
37 write!(f, "{}", parts.join("; "))
38 }
39}
40
41impl std::error::Error for MissingPackDependencies {}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct CircularPackDependency {
46 pub cycle: Vec<String>,
47}
48
49impl fmt::Display for CircularPackDependency {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(
52 f,
53 "circular dependency detected among packs: {}",
54 self.cycle.join(" -> ")
55 )
56 }
57}
58
59impl std::error::Error for CircularPackDependency {}
60
61#[derive(Debug, Error)]
62pub enum RuntimeError {
63 #[error("storage: {0}")]
64 Storage(#[from] khive_storage::StorageError),
65
66 #[error("sqlite: {0}")]
67 Sqlite(#[from] khive_db::SqliteError),
68
69 #[error("query: {0}")]
70 Query(#[from] khive_query::QueryError),
71
72 #[error("not found: {0}")]
73 NotFound(String),
74
75 #[error("invalid input: {0}")]
76 InvalidInput(String),
77
78 #[error("unconfigured: {0} is not set")]
79 Unconfigured(String),
80
81 #[error("unknown embedding model: {0}")]
82 UnknownModel(String),
83
84 #[error("embedding: {0}")]
85 Embedding(#[from] lattice_embed::EmbedError),
86
87 #[error("ambiguous: {0}")]
88 Ambiguous(String),
89
90 #[error("internal: {0}")]
91 Internal(String),
92
93 #[error("missing pack dependency: {0}")]
94 MissingPackDependency(MissingPackDependency),
95
96 #[error("missing pack dependencies: {0}")]
97 MissingPackDependencies(MissingPackDependencies),
98
99 #[error("{0}")]
100 CircularPackDependency(CircularPackDependency),
101
102 #[error("pack '{name}' registered twice (indices {first_idx} and {second_idx})")]
103 PackRedeclared {
104 name: String,
105 first_idx: usize,
106 second_idx: usize,
107 },
108
109 #[error(
113 "verb collision: verb {verb:?} declared by both pack {first_pack:?} and pack \
114 {second_pack:?}; rename one handler or use Visibility::Subhandler for internal verbs"
115 )]
116 VerbCollision {
117 verb: String,
118 first_pack: String,
119 second_pack: String,
120 },
121
122 #[error("permission denied for verb {verb:?}: {reason}")]
128 PermissionDenied { verb: String, reason: String },
129
130 #[error("{0}")]
134 Khive(khive_types::KhiveError),
135
136 #[error("not found in this namespace")]
141 NamespaceMismatch { id: uuid::Uuid },
142
143 #[error("ambiguous prefix {prefix:?}: matches {}", format_uuid_list(matches))]
149 AmbiguousPrefix {
150 prefix: String,
151 matches: Vec<uuid::Uuid>,
152 },
153
154 #[error(
159 "cross-backend merge is not supported: \
160 into_id {into_id} is on backend '{into_backend}', \
161 from_id {from_id} is on backend '{from_backend}'. \
162 Both entities must be on the same backend to merge."
163 )]
164 CrossBackendMergeUnsupported {
165 into_id: uuid::Uuid,
166 from_id: uuid::Uuid,
167 into_backend: String,
168 from_backend: String,
169 },
170
171 #[error("unknown remote: {name:?}")]
174 UnknownRemote { name: String },
175
176 #[error("remote cache missing for remote={remote:?} namespace={namespace:?}")]
178 RemoteCacheMissing { remote: String, namespace: String },
179
180 #[error("ambiguous id {id:?}: matched {count} records")]
182 AmbiguousId { id: String, count: usize },
183
184 #[error("cross-namespace write denied: cannot write to remote namespace {namespace:?}")]
186 CrossNamespaceWrite { namespace: String },
187
188 #[error("remote fetch error for remote={remote:?}: {message}")]
190 RemoteFetchError { remote: String, message: String },
191}
192
193fn format_uuid_list(uuids: &[uuid::Uuid]) -> String {
194 let shorts: Vec<String> = uuids
195 .iter()
196 .map(|u| u.to_string()[..8].to_string())
197 .collect();
198 shorts.join(", ")
199}
200
201impl From<khive_types::KhiveError> for RuntimeError {
202 fn from(e: khive_types::KhiveError) -> Self {
203 Self::Khive(e)
204 }
205}