systemprompt_sync/
error.rs1use thiserror::Error;
2
3#[derive(Debug, Error)]
4pub enum SyncError {
5 #[error("API error {status}: {message}")]
6 ApiError { status: u16, message: String },
7
8 #[error("Unauthorized - run 'systemprompt cloud login'")]
9 Unauthorized,
10
11 #[error("Tenant has no associated app")]
12 TenantNoApp,
13
14 #[error("Must run from project root (with infrastructure/ directory)")]
15 NotProjectRoot,
16
17 #[error("Command failed: {command}")]
18 CommandFailed { command: String },
19
20 #[error("Failed to spawn `{command}`: {source}")]
21 CommandSpawnFailed {
22 command: String,
23 #[source]
24 source: std::io::Error,
25 },
26
27 #[error("Failed to open file {path}: {source}")]
28 FileOpenFailed {
29 path: String,
30 #[source]
31 source: std::io::Error,
32 },
33
34 #[error("Docker login failed")]
35 DockerLoginFailed,
36
37 #[error("Git SHA unavailable")]
38 GitShaUnavailable,
39
40 #[error("Missing configuration: {0}")]
41 MissingConfig(String),
42
43 #[error("Partial import failure after {completed}/{total} items: {message}")]
44 PartialImport {
45 completed: usize,
46 total: usize,
47 message: String,
48 },
49
50 #[error("Unsafe tarball entry rejected: {0}")]
51 TarballUnsafe(String),
52
53 #[error("IO error: {0}")]
54 Io(#[from] std::io::Error),
55
56 #[error("HTTP error: {0}")]
57 Http(#[from] reqwest::Error),
58
59 #[error("JSON error: {0}")]
60 Json(#[from] serde_json::Error),
61
62 #[error("Database error: {0}")]
63 Database(#[from] sqlx::Error),
64
65 #[error("Path error: {0}")]
66 StripPrefix(#[from] std::path::StripPrefixError),
67
68 #[error("Zip error: {0}")]
69 Zip(#[from] zip::result::ZipError),
70}
71
72impl SyncError {
73 pub const fn is_retryable(&self) -> bool {
74 matches!(self, Self::Http(_))
75 || matches!(
76 self,
77 Self::ApiError { status, .. }
78 if *status == 502 || *status == 503 || *status == 504 || *status == 429
79 )
80 }
81}
82
83pub type SyncResult<T> = Result<T, SyncError>;