panproto_vcs/error.rs
1//! Error types for the VCS engine.
2
3use std::fmt;
4
5/// All errors produced by the VCS engine.
6#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum VcsError {
9 /// An object was not found in the store.
10 #[error("object not found: {id}")]
11 ObjectNotFound {
12 /// The missing object's ID.
13 id: crate::ObjectId,
14 },
15
16 /// A ref was not found.
17 #[error("ref not found: {name}")]
18 RefNotFound {
19 /// The missing ref name.
20 name: String,
21 },
22
23 /// HEAD is detached when a branch was expected.
24 #[error("HEAD is detached")]
25 DetachedHead,
26
27 /// Nothing is staged for commit.
28 #[error("nothing staged")]
29 NothingStaged,
30
31 /// Staging validation failed.
32 #[error("validation failed: {reasons:?}")]
33 ValidationFailed {
34 /// The validation errors.
35 reasons: Vec<String>,
36 },
37
38 /// Merge produced conflicts.
39 #[error("merge conflict: {count} conflict(s)")]
40 MergeConflicts {
41 /// The number of conflicts.
42 count: usize,
43 },
44
45 /// Pushout verification of a merge failed.
46 ///
47 /// Raised when the cocone conditions of the merge pushout do not
48 /// hold: a migration is not total, or the two base-to-merged paths
49 /// disagree on a shared base vertex. Surfaced from the production
50 /// merge, rebase, and cherry-pick paths so a mathematically invalid
51 /// merge fails loudly instead of committing a wrong schema.
52 #[error("pushout verification failed: {0}")]
53 PushoutVerification(#[from] crate::merge::PushoutError),
54
55 /// A branch already exists.
56 #[error("branch already exists: {name}")]
57 BranchExists {
58 /// The branch name.
59 name: String,
60 },
61
62 /// Not inside a panproto repository.
63 #[error("not a panproto repository")]
64 NotARepository,
65
66 /// An expected object had the wrong type.
67 #[error("expected {expected} object, found {found}")]
68 WrongObjectType {
69 /// The expected object type.
70 expected: &'static str,
71 /// The actual object type.
72 found: &'static str,
73 },
74
75 /// I/O error.
76 #[error("io error: {0}")]
77 Io(#[from] std::io::Error),
78
79 /// Serialization / deserialization error.
80 #[error("serialization error: {0}")]
81 Serialization(SerializationError),
82
83 /// Migration composition error.
84 #[error("compose error: {0}")]
85 Compose(#[from] panproto_mig::ComposeError),
86
87 /// No common ancestor found for merge.
88 #[error("no common ancestor found")]
89 NoCommonAncestor,
90
91 /// No path found between two commits.
92 #[error("no path found between commits")]
93 NoPath,
94
95 /// Branch is not fully merged into HEAD.
96 #[error("branch '{name}' is not fully merged")]
97 BranchNotMerged {
98 /// The branch name.
99 name: String,
100 },
101
102 /// A merge or cherry-pick is already in progress.
103 #[error("a {operation} is already in progress")]
104 OperationInProgress {
105 /// The operation type (e.g. "merge", "cherry-pick").
106 operation: String,
107 },
108
109 /// Feature is not yet implemented.
110 #[error("{feature} is not yet implemented")]
111 NotImplemented {
112 /// Description of the unimplemented feature.
113 feature: String,
114 },
115
116 /// Merge cannot fast-forward but --ff-only was requested.
117 #[error("cannot fast-forward; refusing to merge")]
118 FastForwardOnly,
119
120 /// Amend requested but no commits exist.
121 #[error("nothing to amend")]
122 NothingToAmend,
123
124 /// A tag already exists.
125 #[error("tag already exists: {name}")]
126 TagExists {
127 /// The tag name.
128 name: String,
129 },
130
131 /// Data migration failed.
132 #[error("data migration failed: {reason}")]
133 DataMigrationFailed {
134 /// Description of the failure.
135 reason: String,
136 },
137
138 /// An object had the wrong type (owned variant for runtime strings).
139 #[error("type mismatch: expected {expected}, got {got}")]
140 TypeMismatch {
141 /// The expected type name.
142 expected: String,
143 /// The actual type name.
144 got: String,
145 },
146
147 /// A version-control square failed to commute: migrating a data set forward
148 /// and back through a schema change did not reconstruct the original.
149 #[error("non-commuting square: {detail}")]
150 SquareNonCommuting {
151 /// Which data set diverged, and how.
152 detail: String,
153 },
154
155 /// I/O error from a string description (for cases where
156 /// `std::io::Error` is not directly available).
157 #[error("io: {0}")]
158 IoError(String),
159
160 /// Two leaves were supplied at the same schema-tree path.
161 ///
162 /// Returned by [`crate::tree::build_tree_from_leaves`] when the
163 /// same path appears twice. Surfacing the duplicate lets callers
164 /// decide how to dedupe rather than silently picking a winner.
165 #[error("duplicate schema-tree path: {path}")]
166 DuplicatePath {
167 /// The offending path, displayed with forward slashes.
168 path: String,
169 },
170
171 /// A leaf was supplied with an empty path.
172 ///
173 /// Returned by [`crate::tree::build_tree_from_leaves`] when a path
174 /// has zero components. Empty paths cannot be placed in the
175 /// directory hierarchy and must not be silently dropped.
176 #[error("empty schema-tree path")]
177 EmptyPath,
178
179 /// A generic error described by a human-readable string.
180 #[error("{0}")]
181 Other(String),
182}
183
184/// Wrapper for serialization errors from rmp-serde.
185#[derive(Debug)]
186pub struct SerializationError(pub String);
187
188impl fmt::Display for SerializationError {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 f.write_str(&self.0)
191 }
192}
193
194impl From<rmp_serde::encode::Error> for VcsError {
195 fn from(e: rmp_serde::encode::Error) -> Self {
196 Self::Serialization(SerializationError(e.to_string()))
197 }
198}
199
200impl From<rmp_serde::decode::Error> for VcsError {
201 fn from(e: rmp_serde::decode::Error) -> Self {
202 Self::Serialization(SerializationError(e.to_string()))
203 }
204}