stackless_stripe_projects/
error.rs1use 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}