use std::fmt;
use thiserror::Error;
pub type RuntimeResult<T> = Result<T, RuntimeError>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MissingPackDependency {
pub from: String,
pub requires: String,
}
impl fmt::Display for MissingPackDependency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"pack '{}' requires '{}', but '{}' is not in the loaded pack set",
self.from, self.requires, self.requires
)
}
}
impl std::error::Error for MissingPackDependency {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MissingPackDependencies {
pub missing: Vec<MissingPackDependency>,
}
impl fmt::Display for MissingPackDependencies {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let parts: Vec<String> = self.missing.iter().map(ToString::to_string).collect();
write!(f, "{}", parts.join("; "))
}
}
impl std::error::Error for MissingPackDependencies {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CircularPackDependency {
pub cycle: Vec<String>,
}
impl fmt::Display for CircularPackDependency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"circular dependency detected among packs: {}",
self.cycle.join(" -> ")
)
}
}
impl std::error::Error for CircularPackDependency {}
#[derive(Debug, Error)]
pub enum RuntimeError {
#[error("storage: {0}")]
Storage(#[from] khive_storage::StorageError),
#[error("sqlite: {0}")]
Sqlite(#[from] khive_db::SqliteError),
#[error("query: {0}")]
Query(#[from] khive_query::QueryError),
#[error("not found: {0}")]
NotFound(String),
#[error("invalid input: {0}")]
InvalidInput(String),
#[error("unconfigured: {0} is not set")]
Unconfigured(String),
#[error("unknown embedding model: {0}")]
UnknownModel(String),
#[error("embedding: {0}")]
Embedding(#[from] lattice_embed::EmbedError),
#[error("ambiguous: {0}")]
Ambiguous(String),
#[error("fusion: {0}")]
Fusion(#[from] khive_fusion::FuseError),
#[error("internal: {0}")]
Internal(String),
#[error("missing pack dependency: {0}")]
MissingPackDependency(MissingPackDependency),
#[error("missing pack dependencies: {0}")]
MissingPackDependencies(MissingPackDependencies),
#[error("{0}")]
CircularPackDependency(CircularPackDependency),
#[error("pack '{name}' registered twice (indices {first_idx} and {second_idx})")]
PackRedeclared {
name: String,
first_idx: usize,
second_idx: usize,
},
#[error(
"verb collision: verb {verb:?} declared by both pack {first_pack:?} and pack \
{second_pack:?}; rename one handler or use Visibility::Subhandler for internal verbs"
)]
VerbCollision {
verb: String,
first_pack: String,
second_pack: String,
},
#[error("permission denied for verb {verb:?}: {reason}")]
PermissionDenied { verb: String, reason: String },
#[error("{0}")]
Khive(khive_types::KhiveError),
#[error("not found in this namespace")]
NamespaceMismatch { id: uuid::Uuid },
#[error("ambiguous prefix {prefix:?}: matches {}", format_uuid_list(matches))]
AmbiguousPrefix {
prefix: String,
matches: Vec<uuid::Uuid>,
},
#[error(
"cross-backend merge is not supported: \
into_id {into_id} is on backend '{into_backend}', \
from_id {from_id} is on backend '{from_backend}'. \
Both entities must be on the same backend to merge."
)]
CrossBackendMergeUnsupported {
into_id: uuid::Uuid,
from_id: uuid::Uuid,
into_backend: String,
from_backend: String,
},
#[error("unknown remote: {name:?}")]
UnknownRemote { name: String },
#[error("remote cache missing for remote={remote:?} namespace={namespace:?}")]
RemoteCacheMissing { remote: String, namespace: String },
#[error("ambiguous id {id:?}: matched {count} records")]
AmbiguousId { id: String, count: usize },
#[error("cross-namespace write denied: cannot write to remote namespace {namespace:?}")]
CrossNamespaceWrite { namespace: String },
#[error("remote fetch error for remote={remote:?}: {message}")]
RemoteFetchError { remote: String, message: String },
#[error(
"write budget exceeded: max_new_entries={max_new_entries}, \
attempted_new_entries={attempted_new_entries}"
)]
WriteBudgetExceeded {
max_new_entries: u64,
attempted_new_entries: u64,
},
}
fn format_uuid_list(uuids: &[uuid::Uuid]) -> String {
let shorts: Vec<String> = uuids
.iter()
.map(|u| u.to_string()[..8].to_string())
.collect();
shorts.join(", ")
}
impl From<khive_types::KhiveError> for RuntimeError {
fn from(e: khive_types::KhiveError) -> Self {
Self::Khive(e)
}
}