khive_vcs/error.rs
1// Copyright 2026 khive contributors. Licensed under Apache-2.0.
2//
3//! Error types for the VCS layer.
4
5use thiserror::Error;
6
7use crate::types::SnapshotId;
8
9#[derive(Debug, Error)]
10pub enum VcsError {
11 /// A snapshot with this ID already exists in the database.
12 /// This should only occur on SHA-256 hash collision (computationally infeasible)
13 /// or if `commit()` is called twice with identical namespace state.
14 #[error("snapshot already exists: {0}")]
15 SnapshotAlreadyExists(SnapshotId),
16
17 /// The requested snapshot archive is not in the local database.
18 /// Callers must `pull` from a remote to fetch it.
19 #[error("snapshot not found: {0}")]
20 SnapshotNotFound(SnapshotId),
21
22 /// No branch with this name in the namespace.
23 #[error("branch not found: {namespace}/{name}")]
24 BranchNotFound { namespace: String, name: String },
25
26 /// The remote branch HEAD is not an ancestor of the local HEAD.
27 /// Caller must `pull`, merge, commit, then push.
28 #[error("non-fast-forward: local={local_head}, remote={remote_head}")]
29 NonFastForward {
30 local_head: SnapshotId,
31 remote_head: SnapshotId,
32 },
33
34 /// The remote khive-sync server could not be reached.
35 #[error("remote unreachable: {url} — {cause}")]
36 RemoteUnreachable { url: String, cause: String },
37
38 /// The remote rejected the request due to authentication failure.
39 #[error("authentication failed for remote: {url}")]
40 AuthFailed { url: String },
41
42 /// The archive stored at the remote has a different hash than expected.
43 /// Indicates corruption or tampering.
44 #[error("hash mismatch: expected {expected}, actual {actual}")]
45 HashMismatch {
46 expected: SnapshotId,
47 actual: SnapshotId,
48 },
49
50 /// The remote has diverged from local history; a merge is required.
51 #[error("merge required: remote history has diverged from local")]
52 MergeRequired,
53
54 /// `checkout` was blocked because there are uncommitted changes.
55 /// Pass `force: true` to discard them.
56 #[error("uncommitted changes: {count} entities/edges modified since last commit")]
57 UncommittedChanges { count: usize },
58
59 /// `merge_branch` was called but no `MergeEngine` has been registered.
60 /// Ships as the default until `khive-merge` is linked.
61 #[error("merge not implemented: link khive-merge to enable three-way merge")]
62 MergeNotImplemented,
63
64 /// A `SnapshotId` string failed validation.
65 #[error("invalid snapshot id: {0}")]
66 InvalidSnapshotId(String),
67
68 /// A branch name failed validation (must match `^[a-zA-Z0-9_-]{1,64}$`).
69 #[error("invalid branch name: {0:?}")]
70 InvalidBranchName(String),
71
72 /// An underlying storage operation failed.
73 #[error("storage: {0}")]
74 Storage(String),
75
76 /// JSON serialization or deserialization failed.
77 #[error("json: {0}")]
78 Json(#[from] serde_json::Error),
79
80 /// An I/O operation failed (file system, network).
81 #[error("io: {0}")]
82 Io(#[from] std::io::Error),
83
84 /// An unexpected internal error.
85 #[error("internal: {0}")]
86 Internal(String),
87}
88
89impl From<khive_runtime::error::RuntimeError> for VcsError {
90 fn from(e: khive_runtime::error::RuntimeError) -> Self {
91 VcsError::Storage(e.to_string())
92 }
93}