Skip to main content

cf_resource_group_sdk/
error.rs

1// Created: 2026-04-16 by Constructor Tech
2// @cpt-dod:cpt-cf-resource-group-dod-sdk-foundation-sdk-errors:p1
3//! Public error types for the resource-group module.
4//!
5//! These errors are safe to expose to other modules and consumers.
6
7use thiserror::Error;
8
9/// Errors that can be returned by the `ResourceGroupClient`.
10#[derive(Error, Debug, Clone)]
11pub enum ResourceGroupError {
12    /// Resource with the specified identifier was not found.
13    #[error("Resource not found: {code}")]
14    NotFound { code: String },
15
16    /// A resource with the specified code already exists.
17    #[error("Resource already exists: {code}")]
18    TypeAlreadyExists { code: String },
19
20    /// Validation error with the provided data.
21    #[error("Validation error: {message}")]
22    Validation { message: String },
23
24    /// Removing allowed parents or disabling root placement would break
25    /// existing group hierarchy relationships.
26    #[error("Allowed parents violation: {message}")]
27    AllowedParentTypesViolation { message: String },
28
29    /// Cannot delete a type because groups of this type still exist.
30    #[error("Active references exist: {message}")]
31    ConflictActiveReferences { message: String },
32
33    /// A generic conflict (e.g. a concurrency or state conflict not related to references).
34    #[error("Conflict: {message}")]
35    Conflict { message: String },
36
37    /// Parent type is not allowed by the type's `allowed_parent_types` configuration.
38    #[error("Invalid parent type: {message}")]
39    InvalidParentType { message: String },
40
41    /// A cycle would be created in the group hierarchy.
42    #[error("Cycle detected: {message}")]
43    CycleDetected { message: String },
44
45    /// A configured limit (depth, width, etc.) would be exceeded.
46    #[error("Limit violation: {message}")]
47    LimitViolation { message: String },
48
49    /// Cross-tenant link would be created.
50    ///
51    /// Each resource (identified by the pair `(resource_type, resource_id)`)
52    /// belongs to groups in exactly one tenant. Returned by
53    /// `ResourceGroupClient::add_membership` when the target group's tenant
54    /// differs from the tenant of any existing membership for the same
55    /// resource. The resource continues to exist — only the cross-tenant link
56    /// is rejected.
57    #[error("Tenant incompatibility: {message}")]
58    TenantIncompatibility { message: String },
59
60    /// Service is temporarily unavailable.
61    #[error("Service unavailable: {message}")]
62    ServiceUnavailable { message: String },
63
64    /// Access was denied by the authorization policy (PDP denial).
65    #[error("Access denied")]
66    AccessDenied,
67
68    /// An internal error occurred.
69    #[error("Internal error")]
70    Internal,
71}
72
73impl ResourceGroupError {
74    /// Create a `NotFound` error.
75    pub fn not_found(code: impl Into<String>) -> Self {
76        Self::NotFound { code: code.into() }
77    }
78
79    /// Create a `TypeAlreadyExists` error.
80    pub fn type_already_exists(code: impl Into<String>) -> Self {
81        Self::TypeAlreadyExists { code: code.into() }
82    }
83
84    /// Create a `Validation` error.
85    pub fn validation(message: impl Into<String>) -> Self {
86        Self::Validation {
87            message: message.into(),
88        }
89    }
90
91    /// Create an `AllowedParentTypesViolation` error.
92    pub fn allowed_parent_types_violation(message: impl Into<String>) -> Self {
93        Self::AllowedParentTypesViolation {
94            message: message.into(),
95        }
96    }
97
98    /// Create a `ConflictActiveReferences` error.
99    pub fn conflict_active_references(message: impl Into<String>) -> Self {
100        Self::ConflictActiveReferences {
101            message: message.into(),
102        }
103    }
104
105    /// Create a generic `Conflict` error.
106    pub fn conflict(message: impl Into<String>) -> Self {
107        Self::Conflict {
108            message: message.into(),
109        }
110    }
111
112    /// Create an `InvalidParentType` error.
113    pub fn invalid_parent_type(message: impl Into<String>) -> Self {
114        Self::InvalidParentType {
115            message: message.into(),
116        }
117    }
118
119    /// Create a `CycleDetected` error.
120    pub fn cycle_detected(message: impl Into<String>) -> Self {
121        Self::CycleDetected {
122            message: message.into(),
123        }
124    }
125
126    /// Create a `LimitViolation` error.
127    pub fn limit_violation(message: impl Into<String>) -> Self {
128        Self::LimitViolation {
129            message: message.into(),
130        }
131    }
132
133    /// Create a `TenantIncompatibility` error.
134    pub fn tenant_incompatibility(message: impl Into<String>) -> Self {
135        Self::TenantIncompatibility {
136            message: message.into(),
137        }
138    }
139
140    /// Create a `ServiceUnavailable` error.
141    pub fn service_unavailable(message: impl Into<String>) -> Self {
142        Self::ServiceUnavailable {
143            message: message.into(),
144        }
145    }
146
147    /// Create an `AccessDenied` error.
148    #[must_use]
149    pub fn access_denied() -> Self {
150        Self::AccessDenied
151    }
152
153    /// Create an `Internal` error.
154    #[must_use]
155    pub fn internal() -> Self {
156        Self::Internal
157    }
158}