function_sdk_rust/
resource.rs1use pbjson_types::{ListValue, Struct, Value, value::Kind};
4use serde::Serialize;
5use sha2::{Digest, Sha256};
6
7use crate::Error;
8use crate::proto::v1::{Ready, Resource};
9
10pub fn struct_to_json(s: &Struct) -> serde_json::Value {
18 serde_json::Value::Object(
19 s.fields
20 .iter()
21 .map(|(k, v)| (k.clone(), pb_to_json(v)))
22 .collect(),
23 )
24}
25
26pub fn json_to_struct(m: &serde_json::Map<String, serde_json::Value>) -> Struct {
28 Struct {
29 fields: m.iter().map(|(k, v)| (k.clone(), json_to_pb(v))).collect(),
30 }
31}
32
33pub fn pb_to_json(v: &Value) -> serde_json::Value {
42 match &v.kind {
43 None | Some(Kind::NullValue(_)) => serde_json::Value::Null,
44 Some(Kind::NumberValue(n)) => number_to_json(*n),
45 Some(Kind::StringValue(s)) => serde_json::Value::String(s.clone()),
46 Some(Kind::BoolValue(b)) => serde_json::Value::Bool(*b),
47 Some(Kind::StructValue(s)) => struct_to_json(s),
48 Some(Kind::ListValue(l)) => {
49 serde_json::Value::Array(l.values.iter().map(pb_to_json).collect())
50 }
51 }
52}
53
54fn number_to_json(n: f64) -> serde_json::Value {
55 const SAFE_INT: f64 = (1i64 << 53) as f64;
56 if n.fract() == 0.0 && n.abs() <= SAFE_INT {
57 return serde_json::Value::Number(serde_json::Number::from(n as i64));
58 }
59 serde_json::Number::from_f64(n)
60 .map(serde_json::Value::Number)
61 .unwrap_or(serde_json::Value::Null)
62}
63
64pub fn json_to_pb(v: &serde_json::Value) -> Value {
66 let kind = match v {
67 serde_json::Value::Null => Kind::NullValue(0),
68 serde_json::Value::Bool(b) => Kind::BoolValue(*b),
69 serde_json::Value::Number(n) => Kind::NumberValue(n.as_f64().unwrap_or(f64::NAN)),
70 serde_json::Value::String(s) => Kind::StringValue(s.clone()),
71 serde_json::Value::Array(a) => Kind::ListValue(ListValue {
72 values: a.iter().map(json_to_pb).collect(),
73 }),
74 serde_json::Value::Object(m) => Kind::StructValue(json_to_struct(m)),
75 };
76 Value { kind: Some(kind) }
77}
78
79pub fn update<T: Serialize + ?Sized>(r: &mut Resource, source: &T) -> Result<(), Error> {
88 let value = serde_json::to_value(source)?;
89 let serde_json::Value::Object(map) = value else {
90 return Err(Error::NotAnObject);
91 };
92 let target = r.resource.get_or_insert_default();
93 for (k, v) in &map {
94 target.fields.insert(k.clone(), json_to_pb(v));
95 }
96 Ok(())
97}
98
99pub fn update_status<T: Serialize + ?Sized>(r: &mut Resource, status: &T) -> Result<(), Error> {
103 update(
104 r,
105 &serde_json::json!({"status": serde_json::to_value(status)?}),
106 )
107}
108
109pub fn set_ready(r: &mut Resource, ready: Ready) {
114 r.ready = ready as i32;
115}
116
117pub fn add_connection_detail(r: &mut Resource, key: impl Into<String>, value: impl Into<Vec<u8>>) {
122 r.connection_details.insert(key.into(), value.into());
123}
124
125#[derive(Clone, Debug, PartialEq, Eq)]
127pub struct Condition {
128 pub typ: String,
130 pub status: String,
132 pub reason: Option<String>,
134 pub message: Option<String>,
136 pub last_transition_time: Option<String>,
138}
139
140impl Condition {
141 fn unknown(typ: &str) -> Self {
142 Self {
143 typ: typ.to_string(),
144 status: "Unknown".to_string(),
145 reason: None,
146 message: None,
147 last_transition_time: None,
148 }
149 }
150}
151
152pub fn get_condition(resource: Option<&Resource>, typ: &str) -> Condition {
159 let Some(s) = resource.and_then(|r| r.resource.as_ref()) else {
160 return Condition::unknown(typ);
161 };
162 let Some(Kind::StructValue(status)) = s.fields.get("status").and_then(|v| v.kind.as_ref())
163 else {
164 return Condition::unknown(typ);
165 };
166 let Some(Kind::ListValue(conditions)) = status
167 .fields
168 .get("conditions")
169 .and_then(|v| v.kind.as_ref())
170 else {
171 return Condition::unknown(typ);
172 };
173
174 for value in &conditions.values {
175 let Some(Kind::StructValue(c)) = value.kind.as_ref() else {
176 continue;
177 };
178 if get_str(c, "type") != Some(typ) {
179 continue;
180 }
181 return Condition {
182 typ: typ.to_string(),
183 status: get_str(c, "status").unwrap_or("Unknown").to_string(),
184 reason: get_str(c, "reason").map(String::from),
185 message: get_str(c, "message").map(String::from),
186 last_transition_time: get_str(c, "lastTransitionTime").map(String::from),
187 };
188 }
189
190 Condition::unknown(typ)
191}
192
193fn get_str<'a>(s: &'a Struct, key: &str) -> Option<&'a str> {
194 match s.fields.get(key)?.kind.as_ref()? {
195 Kind::StringValue(v) => Some(v),
196 _ => None,
197 }
198}
199
200const DNS_LABEL_MAX: usize = 63;
201const HASH_LEN: usize = 5;
202
203pub fn child_name(parts: &[&str], sep: &str) -> String {
210 let full = parts.join(sep);
211 let digest = Sha256::digest(full.as_bytes());
212 let hash: String = digest
213 .iter()
214 .take(HASH_LEN.div_ceil(2))
215 .map(|b| format!("{b:02x}"))
216 .collect();
217 let hash = &hash[..HASH_LEN];
218 let max_prefix = DNS_LABEL_MAX - HASH_LEN - sep.len();
219 let prefix: String = full.chars().take(max_prefix).collect();
220 let prefix = prefix.trim_end_matches(sep);
221 format!("{prefix}{sep}{hash}")
222}