Skip to main content

cloud_sdk/operation/
metadata.rs

1//! Explicit operation safety and retry classification.
2
3/// Provider operation impact.
4#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
5pub enum OperationImpact {
6    /// The operation cannot change provider state.
7    ReadOnly,
8    /// The operation changes provider state without being inherently destructive.
9    Mutation,
10    /// The operation deletes, disables, resets, detaches, or otherwise destroys state.
11    Destructive,
12}
13
14/// HTTP request semantics independent of provider impact.
15#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub enum RequestSemantics {
17    /// Repeating the request is both read-only and idempotent.
18    Safe,
19    /// Repeating the request has the same intended effect, but it changes state.
20    Idempotent,
21    /// Repeating the request can create an additional or different effect.
22    NonIdempotent,
23}
24
25/// Whether caller-owned retry policy may retry the operation.
26#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
27pub enum RetryEligibility {
28    /// Retrying is not admitted by operation metadata.
29    Never,
30    /// An explicit caller policy may retry eligible transient failures.
31    ExplicitPolicy,
32}
33
34/// Whether executing the operation may directly incur provider charges.
35#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36pub enum CostIntent {
37    /// The operation has no known direct resource cost.
38    NoKnownCost,
39    /// The operation may create or enlarge a billed resource.
40    MayIncurCost,
41}
42
43/// Incoherent operation metadata.
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum OperationMetadataError {
46    /// Read-only operations must use safe request semantics.
47    ReadOnlyMustBeSafe,
48    /// Mutating and destructive operations cannot use safe semantics.
49    StateChangeCannotBeSafe,
50    /// Non-idempotent operations cannot be retry eligible.
51    NonIdempotentRetry,
52}
53
54impl_static_error!(OperationMetadataError,
55    Self::ReadOnlyMustBeSafe => "read-only operation must use safe semantics",
56    Self::StateChangeCannotBeSafe => "state-changing operation cannot use safe semantics",
57    Self::NonIdempotentRetry => "non-idempotent operation cannot be retry eligible",
58);
59
60/// Complete operation safety metadata without permissive defaults.
61///
62/// ```compile_fail
63/// use cloud_sdk::operation::OperationMetadata;
64///
65/// let _: OperationMetadata = Default::default();
66/// ```
67#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
68pub struct OperationMetadata {
69    impact: OperationImpact,
70    semantics: RequestSemantics,
71    retry: RetryEligibility,
72    cost: CostIntent,
73}
74
75impl OperationMetadata {
76    /// Creates complete metadata after checking safety invariants.
77    pub const fn new(
78        impact: OperationImpact,
79        semantics: RequestSemantics,
80        retry: RetryEligibility,
81        cost: CostIntent,
82    ) -> Result<Self, OperationMetadataError> {
83        match (impact, semantics, retry) {
84            (OperationImpact::ReadOnly, semantics, _)
85                if !matches!(semantics, RequestSemantics::Safe) =>
86            {
87                return Err(OperationMetadataError::ReadOnlyMustBeSafe);
88            }
89            (
90                OperationImpact::Mutation | OperationImpact::Destructive,
91                RequestSemantics::Safe,
92                _,
93            ) => {
94                return Err(OperationMetadataError::StateChangeCannotBeSafe);
95            }
96            (_, RequestSemantics::NonIdempotent, RetryEligibility::ExplicitPolicy) => {
97                return Err(OperationMetadataError::NonIdempotentRetry);
98            }
99            _ => {}
100        }
101        Ok(Self {
102            impact,
103            semantics,
104            retry,
105            cost,
106        })
107    }
108
109    /// Returns provider-state impact.
110    #[must_use]
111    pub const fn impact(self) -> OperationImpact {
112        self.impact
113    }
114
115    /// Returns HTTP request semantics.
116    #[must_use]
117    pub const fn semantics(self) -> RequestSemantics {
118        self.semantics
119    }
120
121    /// Returns explicit retry eligibility.
122    #[must_use]
123    pub const fn retry_eligibility(self) -> RetryEligibility {
124        self.retry
125    }
126
127    /// Returns direct cost intent.
128    #[must_use]
129    pub const fn cost_intent(self) -> CostIntent {
130        self.cost
131    }
132}