blob: e246b8a5635613adf0a7690d09b017352660d468 [file] [log] [blame]
Mohammed Naser3415a2a2025-03-06 21:16:12 -05001use serde::{Serialize, Serializer};
2use std::collections::HashMap;
3use uuid::Uuid;
4
5/// Primitive OVSDB Atom types
6#[derive(Debug, Clone, PartialEq)]
7pub enum OvsdbAtom {
8 String(String),
9 Integer(i64),
10 Real(f64),
11 Boolean(bool),
12 Uuid(Uuid),
13 NamedUuid(String),
14}
15
16/// OVSDB Value types (atom, set, or map)
17#[derive(Debug, Clone, PartialEq)]
18pub enum OvsdbValue {
19 Atom(OvsdbAtom),
20 Set(Vec<OvsdbAtom>),
21 Map(Vec<(OvsdbAtom, OvsdbAtom)>),
22}
23
24/// Trait for converting between Rust types and OVSDB Values
25pub trait OvsdbSerializable: Sized {
26 fn to_ovsdb(&self) -> OvsdbValue;
27 fn from_ovsdb(value: &OvsdbValue) -> Option<Self>;
28}
29
30impl<T: OvsdbSerializable> OvsdbSerializable for Option<T> {
31 fn to_ovsdb(&self) -> OvsdbValue {
32 match self {
33 Some(val) => val.to_ovsdb(),
34 None => OvsdbValue::Set(vec![]), // Empty set for None
35 }
36 }
37
38 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
39 T::from_ovsdb(value).map(Some)
40 }
41}
42
43impl OvsdbSerializable for String {
44 fn to_ovsdb(&self) -> OvsdbValue {
45 OvsdbValue::Atom(OvsdbAtom::String(self.clone()))
46 }
47
48 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
49 match value {
50 OvsdbValue::Atom(OvsdbAtom::String(s)) => Some(s.clone()),
51 _ => None,
52 }
53 }
54}
55
56impl OvsdbSerializable for i64 {
57 fn to_ovsdb(&self) -> OvsdbValue {
58 OvsdbValue::Atom(OvsdbAtom::Integer(*self))
59 }
60
61 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
62 match value {
63 OvsdbValue::Atom(OvsdbAtom::Integer(i)) => Some(*i),
64 _ => None,
65 }
66 }
67}
68
69impl OvsdbSerializable for f64 {
70 fn to_ovsdb(&self) -> OvsdbValue {
71 OvsdbValue::Atom(OvsdbAtom::Real(*self))
72 }
73
74 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
75 match value {
76 OvsdbValue::Atom(OvsdbAtom::Real(r)) => Some(*r),
77 _ => None,
78 }
79 }
80}
81
82impl OvsdbSerializable for bool {
83 fn to_ovsdb(&self) -> OvsdbValue {
84 OvsdbValue::Atom(OvsdbAtom::Boolean(*self))
85 }
86
87 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
88 match value {
89 OvsdbValue::Atom(OvsdbAtom::Boolean(b)) => Some(*b),
90 _ => None,
91 }
92 }
93}
94
95impl OvsdbSerializable for Uuid {
96 fn to_ovsdb(&self) -> OvsdbValue {
97 OvsdbValue::Atom(OvsdbAtom::Uuid(*self))
98 }
99
100 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
101 match value {
102 OvsdbValue::Atom(OvsdbAtom::Uuid(uuid)) => Some(*uuid),
103 _ => None,
104 }
105 }
106}
107
108impl<T: OvsdbSerializable> OvsdbSerializable for Vec<T> {
109 fn to_ovsdb(&self) -> OvsdbValue {
110 if self.is_empty() {
111 return OvsdbValue::Set(vec![]);
112 }
113
114 // Try to convert each item to an OvsdbAtom
115 let mut atoms = Vec::with_capacity(self.len());
116 for item in self {
117 match item.to_ovsdb() {
118 OvsdbValue::Atom(atom) => atoms.push(atom),
119 _ => return OvsdbValue::Set(vec![]), // Invalid conversion, return empty set
120 }
121 }
122
123 OvsdbValue::Set(atoms)
124 }
125
126 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
127 match value {
128 OvsdbValue::Set(atoms) => {
129 let mut result = Vec::with_capacity(atoms.len());
130 for atom in atoms {
131 if let Some(item) = T::from_ovsdb(&OvsdbValue::Atom(atom.clone())) {
132 result.push(item);
133 } else {
134 return None;
135 }
136 }
137 Some(result)
138 }
139 // Handle single atom as a one-element set
140 OvsdbValue::Atom(atom) => {
141 T::from_ovsdb(&OvsdbValue::Atom(atom.clone())).map(|item| vec![item])
142 }
143 _ => None,
144 }
145 }
146}
147
148impl<K: OvsdbSerializable + ToString + Eq + std::hash::Hash, V: OvsdbSerializable> OvsdbSerializable
149 for HashMap<K, V>
150{
151 fn to_ovsdb(&self) -> OvsdbValue {
152 let mut pairs = Vec::with_capacity(self.len());
153
154 for (key, value) in self {
155 if let OvsdbValue::Atom(key_atom) = key.to_ovsdb() {
156 if let OvsdbValue::Atom(value_atom) = value.to_ovsdb() {
157 pairs.push((key_atom, value_atom));
158 continue;
159 }
160 }
161 return OvsdbValue::Map(vec![]);
162 }
163
164 OvsdbValue::Map(pairs)
165 }
166
167 fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
168 match value {
169 OvsdbValue::Map(map) => {
170 let mut result = HashMap::with_capacity(map.len());
171
172 for (key, val) in map {
173 if let Some(key_converted) = K::from_ovsdb(&OvsdbValue::Atom(key.clone())) {
174 if let Some(val_converted) = V::from_ovsdb(&OvsdbValue::Atom(val.clone())) {
175 result.insert(key_converted, val_converted);
176 } else {
177 return None;
178 }
179 } else {
180 return None;
181 }
182 }
183
184 Some(result)
185 }
186 _ => None,
187 }
188 }
189}
190
191/// Custom serde serialization format for OvsdbValue
192/// Implements the specific JSON format required by OVSDB
193impl Serialize for OvsdbValue {
194 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195 where
196 S: Serializer,
197 {
198 match self {
199 OvsdbValue::Atom(atom) => atom.serialize(serializer),
200 OvsdbValue::Set(set) => {
201 if set.is_empty() {
202 let empty: Vec<String> = vec![];
203 empty.serialize(serializer)
204 } else if set.len() == 1 {
205 set[0].serialize(serializer)
206 } else {
207 let wrapper = ("set", set);
208 wrapper.serialize(serializer)
209 }
210 }
211 OvsdbValue::Map(map) => {
212 let pairs: Vec<[&OvsdbAtom; 2]> = map.iter().map(|(k, v)| [k, v]).collect();
213 let wrapper = ("map", pairs);
214 wrapper.serialize(serializer)
215 }
216 }
217 }
218}
219
220impl Serialize for OvsdbAtom {
221 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
222 where
223 S: Serializer,
224 {
225 match self {
226 OvsdbAtom::String(s) => s.serialize(serializer),
227 OvsdbAtom::Integer(i) => i.serialize(serializer),
228 OvsdbAtom::Real(r) => r.serialize(serializer),
229 OvsdbAtom::Boolean(b) => b.serialize(serializer),
230 OvsdbAtom::Uuid(uuid) => {
231 let wrapper = ("uuid", uuid.to_string());
232 wrapper.serialize(serializer)
233 }
234 OvsdbAtom::NamedUuid(name) => {
235 let wrapper = ("named-uuid", name);
236 wrapper.serialize(serializer)
237 }
238 }
239 }
240}
241
242/// Extension trait for OvsdbSerializable to handle JSON conversion
243pub trait OvsdbSerializableExt: OvsdbSerializable {
244 fn to_ovsdb_json(&self) -> Option<serde_json::Value> {
245 serde_json::to_value(self.to_ovsdb()).ok()
246 }
247
248 fn from_ovsdb_json(json: &serde_json::Value) -> Option<Self> {
249 // Convert JSON to OvsdbValue
250 let value = json_to_ovsdb_value(json)?;
251 Self::from_ovsdb(&value)
252 }
253}
254
255// Implement the extension trait for all types that implement OvsdbSerializable
256impl<T: OvsdbSerializable> OvsdbSerializableExt for T {}
257
258/// Helper function to extract a UUID from a JSON value
259pub fn extract_uuid(value: &serde_json::Value) -> Option<Uuid> {
260 if let serde_json::Value::Array(arr) = value {
261 if arr.len() == 2 && arr[0] == "uuid" {
262 if let serde_json::Value::String(uuid_str) = &arr[1] {
263 return Uuid::parse_str(uuid_str).ok();
264 }
265 }
266 }
267 None
268}
269
270/// Convert a JSON value to an OvsdbValue
271fn json_to_ovsdb_value(json: &serde_json::Value) -> Option<OvsdbValue> {
272 match json {
273 serde_json::Value::String(s) => Some(OvsdbValue::Atom(OvsdbAtom::String(s.clone()))),
274 serde_json::Value::Number(n) => {
275 if let Some(i) = n.as_i64() {
276 Some(OvsdbValue::Atom(OvsdbAtom::Integer(i)))
277 } else {
278 n.as_f64().map(|f| OvsdbValue::Atom(OvsdbAtom::Real(f)))
279 }
280 }
281 serde_json::Value::Bool(b) => Some(OvsdbValue::Atom(OvsdbAtom::Boolean(*b))),
282 serde_json::Value::Array(arr) => {
283 if arr.len() == 2 {
284 if let serde_json::Value::String(tag) = &arr[0] {
285 match tag.as_str() {
286 "uuid" => {
287 if let serde_json::Value::String(uuid_str) = &arr[1] {
288 if let Ok(uuid) = Uuid::parse_str(uuid_str) {
289 return Some(OvsdbValue::Atom(OvsdbAtom::Uuid(uuid)));
290 }
291 }
292 }
293 "named-uuid" => {
294 if let serde_json::Value::String(name) = &arr[1] {
295 return Some(OvsdbValue::Atom(OvsdbAtom::NamedUuid(name.clone())));
296 }
297 }
298 "set" => {
299 if let serde_json::Value::Array(elements) = &arr[1] {
300 let mut atoms = Vec::with_capacity(elements.len());
301 for elem in elements {
302 if let Some(OvsdbValue::Atom(atom)) = json_to_ovsdb_value(elem)
303 {
304 atoms.push(atom);
305 } else {
306 return None;
307 }
308 }
309 return Some(OvsdbValue::Set(atoms));
310 }
311 }
312 "map" => {
313 if let serde_json::Value::Array(pairs) = &arr[1] {
314 let mut map_pairs = Vec::with_capacity(pairs.len());
315 for pair in pairs {
316 if let serde_json::Value::Array(kv) = pair {
317 if kv.len() == 2 {
318 if let (
319 Some(OvsdbValue::Atom(key)),
320 Some(OvsdbValue::Atom(value)),
321 ) = (
322 json_to_ovsdb_value(&kv[0]),
323 json_to_ovsdb_value(&kv[1]),
324 ) {
325 map_pairs.push((key, value));
326 continue;
327 }
328 }
329 }
330 return None;
331 }
332 return Some(OvsdbValue::Map(map_pairs));
333 }
334 }
335 _ => {}
336 }
337 }
338 }
339
340 // Empty array means empty set
341 if arr.is_empty() {
342 return Some(OvsdbValue::Set(vec![]));
343 }
344
345 None
346 }
347 serde_json::Value::Null => {
348 // Null is represented as an empty set
349 Some(OvsdbValue::Set(vec![]))
350 }
351 _ => None,
352 }
353}