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}