Skip to main content

systemprompt_sync/
error.rs

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