Skip to main content

stackless_stripe_projects/
error.rs

1//! Stripe Projects errors (neutral `stripe.projects.*` fault codes).
2
3use stackless_core::fault::{ErrorContext, Fault, codes};
4
5#[derive(Debug, thiserror::Error)]
6pub enum ProjectsError {
7    #[error("the Stripe CLI or its `projects` plugin is unavailable: {detail}")]
8    Unavailable { detail: String },
9
10    #[error("Stripe Projects is not authenticated: {detail}")]
11    Auth { detail: String },
12
13    #[error("`stripe projects {command}` failed: {detail}")]
14    Failed { command: String, detail: String },
15
16    #[error(
17        "another stackless process holds the Stripe Projects lock for {definition_dir}: {detail}"
18    )]
19    LockHeld {
20        definition_dir: String,
21        detail: String,
22    },
23
24    #[error("`stripe projects` timed out after {budget_secs}s ({detail})")]
25    Timeout { budget_secs: u64, detail: String },
26
27    #[error("cannot anchor the stack's Stripe project: {detail}")]
28    ProjectAnchor { detail: String },
29
30    #[error("provisioning {resource:?} via Stripe Projects did not complete: {detail}")]
31    ProvisionFailed { resource: String, detail: String },
32
33    #[error("the Stripe Projects catalog has no service {reference:?}")]
34    CatalogMissing { reference: String },
35
36    #[error("config for {reference:?} does not match the Stripe Projects catalog: {}", violations.join("; "))]
37    ConfigSchema {
38        reference: &'static str,
39        violations: Vec<String>,
40    },
41}
42
43impl Fault for ProjectsError {
44    fn code(&self) -> &'static str {
45        match self {
46            Self::Unavailable { .. } => codes::STRIPE_PROJECTS_UNAVAILABLE,
47            Self::Auth { .. } => codes::STRIPE_PROJECTS_AUTH,
48            Self::Failed { .. } => codes::STRIPE_PROJECTS_FAILED,
49            Self::LockHeld { .. } => codes::STRIPE_PROJECTS_LOCK_HELD,
50            Self::Timeout { .. } => codes::STRIPE_PROJECTS_TIMEOUT,
51            Self::ProjectAnchor { .. } => codes::STRIPE_PROJECT_ANCHOR,
52            Self::ProvisionFailed { .. } => codes::STRIPE_PROJECTS_PROVISION_FAILED,
53            Self::CatalogMissing { .. } => codes::STRIPE_PROJECTS_CATALOG_MISSING,
54            Self::ConfigSchema { .. } => codes::STRIPE_PROJECTS_CONFIG_SCHEMA,
55        }
56    }
57
58    fn remediation(&self) -> String {
59        match self {
60            Self::Unavailable { .. } => {
61                "install the Stripe CLI (https://docs.stripe.com/stripe-cli), then run \
62                 `stripe plugin install projects`"
63                    .into()
64            }
65            Self::Auth { .. } => "run `stripe login`, then re-run `up`".into(),
66            Self::Failed { command, .. } => {
67                format!(
68                    "run `stackless doctor` to see aggregated preflight blockers, or \
69                     `stripe projects {command}` by hand, then re-run"
70                )
71            }
72            Self::LockHeld { .. } => {
73                "another `stackless up` is provisioning Stripe Projects in this definition dir; \
74                 wait for it to finish or kill the holder, then re-run"
75                    .into()
76            }
77            Self::Timeout { .. } => {
78                "the Stripe Projects CLI did not return in time; kill leftover \
79                 `stripe` / `stripe-cli-projects` processes and re-run"
80                    .into()
81            }
82            Self::ProjectAnchor { .. } => {
83                "ensure the definition dir is writable and `stripe projects status` reports a \
84                 linked project, then re-run `up`"
85                    .into()
86            }
87            Self::ProvisionFailed { .. } => {
88                "wait a minute for the provider to finish provisioning and re-run `up` to resume"
89                    .into()
90            }
91            Self::CatalogMissing { reference } => {
92                let provider = reference
93                    .split_once('/')
94                    .map(|(p, _)| p)
95                    .unwrap_or(reference);
96                format!(
97                    "run `stripe projects catalog {provider} --json`; the filter token may not \
98                     match this provider, or the service may have been renamed or removed"
99                )
100            }
101            Self::ConfigSchema { .. } => {
102                "the service's catalog `configuration_schema` changed; update the plugin's typed \
103                 config to match (the catalog gap test pins this)"
104                    .into()
105            }
106        }
107    }
108
109    fn context(&self) -> ErrorContext {
110        ErrorContext::default()
111    }
112}