continuity_runtime/
slo.rs1use crate::error::{ContinuityValidationError, ContinuityValidationResult};
2use crate::validation::{require_non_empty, require_schema_version};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
7pub struct ServiceLevelProfileV1 {
8 pub schema_version: String,
9 pub service_level_profile_id: String,
10 pub service_name: String,
11 pub availability_target: String,
12 pub latency_target: String,
13 pub owner_ref: String,
14 pub error_budget_ledger_id: String,
15}
16
17impl ServiceLevelProfileV1 {
18 pub const SCHEMA_VERSION: &'static str = "ServiceLevelProfileV1";
19
20 pub fn new(
21 service_level_profile_id: impl Into<String>,
22 service_name: impl Into<String>,
23 availability_target: impl Into<String>,
24 latency_target: impl Into<String>,
25 owner_ref: impl Into<String>,
26 error_budget_ledger_id: impl Into<String>,
27 ) -> Result<Self, ContinuityValidationError> {
28 let value = Self {
29 schema_version: Self::SCHEMA_VERSION.to_string(),
30 service_level_profile_id: service_level_profile_id.into(),
31 service_name: service_name.into(),
32 availability_target: availability_target.into(),
33 latency_target: latency_target.into(),
34 owner_ref: owner_ref.into(),
35 error_budget_ledger_id: error_budget_ledger_id.into(),
36 };
37 value.validate()?;
38 Ok(value)
39 }
40
41 pub fn validate(&self) -> ContinuityValidationResult {
42 require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
43 require_non_empty(&self.service_level_profile_id, "service_level_profile_id")?;
44 require_non_empty(&self.service_name, "service_name")?;
45 require_non_empty(&self.availability_target, "availability_target")?;
46 require_non_empty(&self.latency_target, "latency_target")?;
47 require_non_empty(&self.owner_ref, "owner_ref")?;
48 require_non_empty(&self.error_budget_ledger_id, "error_budget_ledger_id")?;
49 Ok(())
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
54pub struct ErrorBudgetLedgerV1 {
55 pub schema_version: String,
56 pub error_budget_ledger_id: String,
57 pub service_level_profile_id: String,
58 pub window: String,
59 pub budget_consumed_percent: f64,
60 pub remaining_budget_percent: f64,
61 pub last_updated_at: String,
62}
63
64impl ErrorBudgetLedgerV1 {
65 pub const SCHEMA_VERSION: &'static str = "ErrorBudgetLedgerV1";
66
67 pub fn new(
68 error_budget_ledger_id: impl Into<String>,
69 service_level_profile_id: impl Into<String>,
70 window: impl Into<String>,
71 budget_consumed_percent: f64,
72 remaining_budget_percent: f64,
73 last_updated_at: impl Into<String>,
74 ) -> Result<Self, ContinuityValidationError> {
75 let value = Self {
76 schema_version: Self::SCHEMA_VERSION.to_string(),
77 error_budget_ledger_id: error_budget_ledger_id.into(),
78 service_level_profile_id: service_level_profile_id.into(),
79 window: window.into(),
80 budget_consumed_percent,
81 remaining_budget_percent,
82 last_updated_at: last_updated_at.into(),
83 };
84 value.validate()?;
85 Ok(value)
86 }
87
88 pub fn validate(&self) -> ContinuityValidationResult {
89 require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
90 require_non_empty(&self.error_budget_ledger_id, "error_budget_ledger_id")?;
91 require_non_empty(&self.service_level_profile_id, "service_level_profile_id")?;
92 require_non_empty(&self.window, "window")?;
93 require_non_empty(&self.last_updated_at, "last_updated_at")?;
94 if !(0.0..=100.0).contains(&self.budget_consumed_percent) {
95 return Err(ContinuityValidationError::InvalidState(
96 "budget_consumed_percent",
97 ));
98 }
99 if !(0.0..=100.0).contains(&self.remaining_budget_percent) {
100 return Err(ContinuityValidationError::InvalidState(
101 "remaining_budget_percent",
102 ));
103 }
104 Ok(())
105 }
106}