ovsdb_schema/
lib.rs

1use 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}