Skip to main content

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}