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    /// A branch already exists.
46    #[error("branch already exists: {name}")]
47    BranchExists {
48        /// The branch name.
49        name: String,
50    },
51
52    /// Not inside a panproto repository.
53    #[error("not a panproto repository")]
54    NotARepository,
55
56    /// An expected object had the wrong type.
57    #[error("expected {expected} object, found {found}")]
58    WrongObjectType {
59        /// The expected object type.
60        expected: &'static str,
61        /// The actual object type.
62        found: &'static str,
63    },
64
65    /// I/O error.
66    #[error("io error: {0}")]
67    Io(#[from] std::io::Error),
68
69    /// Serialization / deserialization error.
70    #[error("serialization error: {0}")]
71    Serialization(SerializationError),
72
73    /// Migration composition error.
74    #[error("compose error: {0}")]
75    Compose(#[from] panproto_mig::ComposeError),
76
77    /// No common ancestor found for merge.
78    #[error("no common ancestor found")]
79    NoCommonAncestor,
80
81    /// No path found between two commits.
82    #[error("no path found between commits")]
83    NoPath,
84
85    /// Branch is not fully merged into HEAD.
86    #[error("branch '{name}' is not fully merged")]
87    BranchNotMerged {
88        /// The branch name.
89        name: String,
90    },
91
92    /// A merge or cherry-pick is already in progress.
93    #[error("a {operation} is already in progress")]
94    OperationInProgress {
95        /// The operation type (e.g. "merge", "cherry-pick").
96        operation: String,
97    },
98
99    /// Feature is not yet implemented.
100    #[error("{feature} is not yet implemented")]
101    NotImplemented {
102        /// Description of the unimplemented feature.
103        feature: String,
104    },
105
106    /// Merge cannot fast-forward but --ff-only was requested.
107    #[error("cannot fast-forward; refusing to merge")]
108    FastForwardOnly,
109
110    /// Amend requested but no commits exist.
111    #[error("nothing to amend")]
112    NothingToAmend,
113
114    /// A tag already exists.
115    #[error("tag already exists: {name}")]
116    TagExists {
117        /// The tag name.
118        name: String,
119    },
120
121    /// Data migration failed.
122    #[error("data migration failed: {reason}")]
123    DataMigrationFailed {
124        /// Description of the failure.
125        reason: String,
126    },
127
128    /// An object had the wrong type (owned variant for runtime strings).
129    #[error("type mismatch: expected {expected}, got {got}")]
130    TypeMismatch {
131        /// The expected type name.
132        expected: String,
133        /// The actual type name.
134        got: String,
135    },
136
137    /// I/O error from a string description (for cases where
138    /// `std::io::Error` is not directly available).
139    #[error("io: {0}")]
140    IoError(String),
141
142    /// Two leaves were supplied at the same schema-tree path.
143    ///
144    /// Returned by [`crate::tree::build_tree_from_leaves`] when the
145    /// same path appears twice. Surfacing the duplicate lets callers
146    /// decide how to dedupe rather than silently picking a winner.
147    #[error("duplicate schema-tree path: {path}")]
148    DuplicatePath {
149        /// The offending path, displayed with forward slashes.
150        path: String,
151    },
152
153    /// A leaf was supplied with an empty path.
154    ///
155    /// Returned by [`crate::tree::build_tree_from_leaves`] when a path
156    /// has zero components. Empty paths cannot be placed in the
157    /// directory hierarchy and must not be silently dropped.
158    #[error("empty schema-tree path")]
159    EmptyPath,
160
161    /// A generic error described by a human-readable string.
162    #[error("{0}")]
163    Other(String),
164}
165
166/// Wrapper for serialization errors from rmp-serde.
167#[derive(Debug)]
168pub struct SerializationError(pub String);
169
170impl fmt::Display for SerializationError {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        f.write_str(&self.0)
173    }
174}
175
176impl From<rmp_serde::encode::Error> for VcsError {
177    fn from(e: rmp_serde::encode::Error) -> Self {
178        Self::Serialization(SerializationError(e.to_string()))
179    }
180}
181
182impl From<rmp_serde::decode::Error> for VcsError {
183    fn from(e: rmp_serde::decode::Error) -> Self {
184        Self::Serialization(SerializationError(e.to_string()))
185    }
186}