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("cannot anchor the stack's Stripe project: {detail}")]
25    ProjectAnchor { detail: String },
26
27    #[error("provisioning {resource:?} via Stripe Projects did not complete: {detail}")]
28    ProvisionFailed { resource: String, detail: String },
29
30    #[error("the Stripe Projects catalog has no service {reference:?}")]
31    CatalogMissing { reference: String },
32
33    #[error("config for {reference:?} does not match the Stripe Projects catalog: {}", violations.join("; "))]
34    ConfigSchema {
35        reference: &'static str,
36        violations: Vec<String>,
37    },
38}
39
40impl Fault for ProjectsError {
41    fn code(&self) -> &'static str {
42        match self {
43            Self::Unavailable { .. } => codes::STRIPE_PROJECTS_UNAVAILABLE,
44            Self::Auth { .. } => codes::STRIPE_PROJECTS_AUTH,
45            Self::Failed { .. } => codes::STRIPE_PROJECTS_FAILED,
46            Self::LockHeld { .. } => codes::STRIPE_PROJECTS_LOCK_HELD,
47            Self::ProjectAnchor { .. } => codes::STRIPE_PROJECT_ANCHOR,
48            Self::ProvisionFailed { .. } => codes::STRIPE_PROJECTS_PROVISION_FAILED,
49            Self::CatalogMissing { .. } => codes::STRIPE_PROJECTS_CATALOG_MISSING,
50            Self::ConfigSchema { .. } => codes::STRIPE_PROJECTS_CONFIG_SCHEMA,
51        }
52    }
53
54    fn remediation(&self) -> String {
55        match self {
56            Self::Unavailable { .. } => {
57                "install the Stripe CLI (https://docs.stripe.com/stripe-cli), then run \
58                 `stripe plugin install projects`"
59                    .into()
60            }
61            Self::Auth { .. } => "run `stripe login`, then re-run `up`".into(),
62            Self::Failed { command, .. } => {
63                format!(
64                    "run `stackless doctor` to see aggregated preflight blockers, or \
65                     `stripe projects {command}` by hand, then re-run"
66                )
67            }
68            Self::LockHeld { .. } => {
69                "another `stackless up` is provisioning Stripe Projects in this definition dir; \
70                 wait for it to finish, then re-run `up`"
71                    .into()
72            }
73            Self::ProjectAnchor { .. } => {
74                "ensure the definition dir is writable and `stripe projects status` reports a \
75                 linked project, then re-run `up`"
76                    .into()
77            }
78            Self::ProvisionFailed { .. } => {
79                "wait a minute for the provider to finish provisioning and re-run `up` to resume"
80                    .into()
81            }
82            Self::CatalogMissing { reference } => {
83                let provider = reference
84                    .split_once('/')
85                    .map(|(p, _)| p)
86                    .unwrap_or(reference);
87                format!(
88                    "run `stripe projects catalog {provider} --json`; the filter token may not \
89                     match this provider, or the service may have been renamed or removed"
90                )
91            }
92            Self::ConfigSchema { .. } => {
93                "the service's catalog `configuration_schema` changed; update the plugin's typed \
94                 config to match (the catalog gap test pins this)"
95                    .into()
96            }
97        }
98    }
99
100    fn context(&self) -> ErrorContext {
101        ErrorContext::default()
102    }
103}