stackable_versioned/
lib.rs1use std::collections::BTreeMap;
13
14use schemars::{Schema, json_schema};
15use snafu::{ErrorCompat, Snafu};
16pub use stackable_versioned_macros::versioned;
18
19pub trait TrackingFrom<T, S>
24where
25 Self: Sized,
26 S: TrackingStatus + Default,
27{
28 fn tracking_from(value: T, status: &mut S, parent: &str) -> Self;
30}
31
32pub trait TrackingInto<T, S>
38where
39 Self: Sized,
40 S: TrackingStatus + Default,
41{
42 fn tracking_into(self, status: &mut S, parent: &str) -> T;
44}
45
46impl<T, U, S> TrackingInto<U, S> for T
47where
48 S: TrackingStatus + Default,
49 U: TrackingFrom<T, S>,
50{
51 fn tracking_into(self, status: &mut S, parent: &str) -> U {
52 U::tracking_from(self, status, parent)
53 }
54}
55
56pub trait TrackingStatus {
58 fn changes(&mut self) -> &mut ChangedValues;
59}
60
61#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
68pub struct ChangedValues {
69 pub downgrades: BTreeMap<String, Vec<ChangedValue>>,
71
72 pub upgrades: BTreeMap<String, Vec<ChangedValue>>,
74 }
77
78#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
80#[serde(rename_all = "camelCase")]
81pub struct ChangedValue {
82 pub json_path: String,
84
85 #[schemars(schema_with = "raw_object_schema")]
87 pub value: serde_yaml::Value,
88}
89
90fn raw_object_schema(_: &mut schemars::generate::SchemaGenerator) -> Schema {
94 json_schema!({
95 "type": "object",
96 "x-kubernetes-preserve-unknown-fields": true,
97 })
98}
99
100#[derive(Debug, Snafu)]
102pub enum ParseObjectError {
103 #[snafu(display("the field {field:?} is missing"))]
104 FieldNotPresent { field: String },
105
106 #[snafu(display("the field {field:?} must be a string"))]
107 FieldNotStr { field: String },
108
109 #[snafu(display("encountered unknown object API version {api_version:?}"))]
110 UnknownApiVersion { api_version: String },
111
112 #[snafu(display("failed to deserialize object from JSON"))]
113 Deserialize { source: serde_json::Error },
114
115 #[snafu(display("unexpected object kind {kind:?}, expected {expected:?}"))]
116 UnexpectedKind { kind: String, expected: String },
117}
118
119#[derive(Debug, Snafu)]
122pub enum ConvertObjectError {
123 #[snafu(display("failed to parse object"))]
124 Parse { source: ParseObjectError },
125
126 #[snafu(display("failed to serialize object into json"))]
127 Serialize { source: serde_json::Error },
128
129 #[snafu(display("failed to parse desired API version"))]
130 ParseDesiredApiVersion {
131 source: UnknownDesiredApiVersionError,
132 },
133}
134
135impl ConvertObjectError {
136 pub fn join_errors(&self) -> String {
138 self.iter_chain()
143 .map(|err| err.to_string())
144 .collect::<Vec<String>>()
145 .join(": ")
146 }
147
148 pub fn http_status_code(&self) -> u16 {
150 match self {
151 ConvertObjectError::Parse { .. } => 400,
152 ConvertObjectError::Serialize { .. } => 500,
153
154 ConvertObjectError::ParseDesiredApiVersion {
156 source: UnknownDesiredApiVersionError { .. },
157 } => 400,
158 }
159 }
160}
161
162#[derive(Debug, Snafu)]
163#[snafu(display("unknown API version {api_version:?}"))]
164pub struct UnknownDesiredApiVersionError {
165 pub api_version: String,
166}
167
168pub fn jthong_path(parent: &str, child: &str) -> String {
169 format!("{parent}.{child}")
170}