Skip to main content

reposix_core/
error.rs

1//! Error types shared by every crate.
2
3use thiserror::Error;
4
5/// Convenience alias for results in the reposix workspace.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// All errors that can flow across crate boundaries.
9#[derive(Debug, Error)]
10pub enum Error {
11    /// Frontmatter parsing or serialization failure.
12    #[error("frontmatter: {0}")]
13    Frontmatter(String),
14
15    /// Record body / file format violation.
16    #[error("invalid record file: {0}")]
17    InvalidRecord(String),
18
19    /// Remote URL could not be parsed into a [`RemoteSpec`](crate::RemoteSpec).
20    #[error("invalid remote url: {0}")]
21    InvalidRemote(String),
22
23    /// Underlying I/O error.
24    #[error(transparent)]
25    Io(#[from] std::io::Error),
26
27    /// JSON serialization error.
28    #[error(transparent)]
29    Json(#[from] serde_json::Error),
30
31    /// YAML serialization error.
32    #[error(transparent)]
33    Yaml(#[from] serde_yaml::Error),
34
35    /// Untyped error escape hatch — only for cases where typing the error adds no value.
36    #[error("{0}")]
37    Other(String),
38
39    /// URL rejected by the egress allowlist (SG-01).
40    #[error("blocked origin: {0}")]
41    InvalidOrigin(String),
42
43    /// Path/filename rejected by the path validator (SG-04).
44    #[error("invalid path: {0}")]
45    InvalidPath(String),
46
47    /// Underlying HTTP/transport error from reqwest.
48    #[error(transparent)]
49    Http(#[from] reqwest::Error),
50
51    /// The requested record does not exist on the backend.
52    ///
53    /// Migrated from `Error::Other(format!("not found: {context}"))` —
54    /// see code-quality audit P1-1 (POLISH2-09 partial). Display string
55    /// retains the `"not found:"` prefix so existing substring-matching
56    /// callers (e.g. `reposix-swarm`'s `ErrorKind::classify`) continue to
57    /// classify correctly during the migration.
58    #[error("not found: {project}/{id}")]
59    NotFound {
60        /// Project / space / repo slug the lookup targeted.
61        project: String,
62        /// Record id (issue id, page id, etc.) that was not found.
63        id: String,
64    },
65
66    /// The backend received the request but does not support the operation
67    /// (e.g., a read-only connector being asked to update). Reserved for
68    /// future migration of the read-only-backend disambiguator currently
69    /// emitted as `Error::Other("not supported: ...")` by adapters such as
70    /// `reposix-jira` (full migration scheduled for v0.12.0).
71    #[error("not supported: {operation}")]
72    NotSupported {
73        /// Human-readable name of the unsupported operation.
74        operation: String,
75    },
76
77    /// Optimistic concurrency check failed: the caller's `version` does
78    /// not match the backend's current version.
79    ///
80    /// Migrated from `Error::Other(format!("version mismatch: {body}"))` —
81    /// see code-quality audit P1-1 + P1-5 (POLISH2-09 partial). Closes the
82    /// stringly-typed protocol where downstream callers used to recover the
83    /// rejection body via `msg.strip_prefix("version mismatch: ")` plus
84    /// `serde_json::from_str` — pattern-match on `body` instead.
85    #[error("version mismatch: current={current} requested={requested}")]
86    VersionMismatch {
87        /// Backend's current version (as reported in the rejection body).
88        current: String,
89        /// Caller-supplied `If-Match` / `expected_version` value.
90        requested: String,
91        /// Full backend response body for callers that want to inspect
92        /// the rejection (e.g., the simulator's HTTP 409 JSON).
93        body: String,
94    },
95}