Initial commit
Change-Id: I2b916ff0acd2a88aeef709cf4f900503e823d44d
diff --git a/schema/src/lib.rs b/schema/src/lib.rs
new file mode 100644
index 0000000..e246b8a
--- /dev/null
+++ b/schema/src/lib.rs
@@ -0,0 +1,353 @@
+use serde::{Serialize, Serializer};
+use std::collections::HashMap;
+use uuid::Uuid;
+
+/// Primitive OVSDB Atom types
+#[derive(Debug, Clone, PartialEq)]
+pub enum OvsdbAtom {
+ String(String),
+ Integer(i64),
+ Real(f64),
+ Boolean(bool),
+ Uuid(Uuid),
+ NamedUuid(String),
+}
+
+/// OVSDB Value types (atom, set, or map)
+#[derive(Debug, Clone, PartialEq)]
+pub enum OvsdbValue {
+ Atom(OvsdbAtom),
+ Set(Vec<OvsdbAtom>),
+ Map(Vec<(OvsdbAtom, OvsdbAtom)>),
+}
+
+/// Trait for converting between Rust types and OVSDB Values
+pub trait OvsdbSerializable: Sized {
+ fn to_ovsdb(&self) -> OvsdbValue;
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self>;
+}
+
+impl<T: OvsdbSerializable> OvsdbSerializable for Option<T> {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ match self {
+ Some(val) => val.to_ovsdb(),
+ None => OvsdbValue::Set(vec![]), // Empty set for None
+ }
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ T::from_ovsdb(value).map(Some)
+ }
+}
+
+impl OvsdbSerializable for String {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ OvsdbValue::Atom(OvsdbAtom::String(self.clone()))
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Atom(OvsdbAtom::String(s)) => Some(s.clone()),
+ _ => None,
+ }
+ }
+}
+
+impl OvsdbSerializable for i64 {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ OvsdbValue::Atom(OvsdbAtom::Integer(*self))
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Atom(OvsdbAtom::Integer(i)) => Some(*i),
+ _ => None,
+ }
+ }
+}
+
+impl OvsdbSerializable for f64 {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ OvsdbValue::Atom(OvsdbAtom::Real(*self))
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Atom(OvsdbAtom::Real(r)) => Some(*r),
+ _ => None,
+ }
+ }
+}
+
+impl OvsdbSerializable for bool {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ OvsdbValue::Atom(OvsdbAtom::Boolean(*self))
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Atom(OvsdbAtom::Boolean(b)) => Some(*b),
+ _ => None,
+ }
+ }
+}
+
+impl OvsdbSerializable for Uuid {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ OvsdbValue::Atom(OvsdbAtom::Uuid(*self))
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Atom(OvsdbAtom::Uuid(uuid)) => Some(*uuid),
+ _ => None,
+ }
+ }
+}
+
+impl<T: OvsdbSerializable> OvsdbSerializable for Vec<T> {
+ fn to_ovsdb(&self) -> OvsdbValue {
+ if self.is_empty() {
+ return OvsdbValue::Set(vec![]);
+ }
+
+ // Try to convert each item to an OvsdbAtom
+ let mut atoms = Vec::with_capacity(self.len());
+ for item in self {
+ match item.to_ovsdb() {
+ OvsdbValue::Atom(atom) => atoms.push(atom),
+ _ => return OvsdbValue::Set(vec![]), // Invalid conversion, return empty set
+ }
+ }
+
+ OvsdbValue::Set(atoms)
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Set(atoms) => {
+ let mut result = Vec::with_capacity(atoms.len());
+ for atom in atoms {
+ if let Some(item) = T::from_ovsdb(&OvsdbValue::Atom(atom.clone())) {
+ result.push(item);
+ } else {
+ return None;
+ }
+ }
+ Some(result)
+ }
+ // Handle single atom as a one-element set
+ OvsdbValue::Atom(atom) => {
+ T::from_ovsdb(&OvsdbValue::Atom(atom.clone())).map(|item| vec![item])
+ }
+ _ => None,
+ }
+ }
+}
+
+impl<K: OvsdbSerializable + ToString + Eq + std::hash::Hash, V: OvsdbSerializable> OvsdbSerializable
+ for HashMap<K, V>
+{
+ fn to_ovsdb(&self) -> OvsdbValue {
+ let mut pairs = Vec::with_capacity(self.len());
+
+ for (key, value) in self {
+ if let OvsdbValue::Atom(key_atom) = key.to_ovsdb() {
+ if let OvsdbValue::Atom(value_atom) = value.to_ovsdb() {
+ pairs.push((key_atom, value_atom));
+ continue;
+ }
+ }
+ return OvsdbValue::Map(vec![]);
+ }
+
+ OvsdbValue::Map(pairs)
+ }
+
+ fn from_ovsdb(value: &OvsdbValue) -> Option<Self> {
+ match value {
+ OvsdbValue::Map(map) => {
+ let mut result = HashMap::with_capacity(map.len());
+
+ for (key, val) in map {
+ if let Some(key_converted) = K::from_ovsdb(&OvsdbValue::Atom(key.clone())) {
+ if let Some(val_converted) = V::from_ovsdb(&OvsdbValue::Atom(val.clone())) {
+ result.insert(key_converted, val_converted);
+ } else {
+ return None;
+ }
+ } else {
+ return None;
+ }
+ }
+
+ Some(result)
+ }
+ _ => None,
+ }
+ }
+}
+
+/// Custom serde serialization format for OvsdbValue
+/// Implements the specific JSON format required by OVSDB
+impl Serialize for OvsdbValue {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ match self {
+ OvsdbValue::Atom(atom) => atom.serialize(serializer),
+ OvsdbValue::Set(set) => {
+ if set.is_empty() {
+ let empty: Vec<String> = vec![];
+ empty.serialize(serializer)
+ } else if set.len() == 1 {
+ set[0].serialize(serializer)
+ } else {
+ let wrapper = ("set", set);
+ wrapper.serialize(serializer)
+ }
+ }
+ OvsdbValue::Map(map) => {
+ let pairs: Vec<[&OvsdbAtom; 2]> = map.iter().map(|(k, v)| [k, v]).collect();
+ let wrapper = ("map", pairs);
+ wrapper.serialize(serializer)
+ }
+ }
+ }
+}
+
+impl Serialize for OvsdbAtom {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ match self {
+ OvsdbAtom::String(s) => s.serialize(serializer),
+ OvsdbAtom::Integer(i) => i.serialize(serializer),
+ OvsdbAtom::Real(r) => r.serialize(serializer),
+ OvsdbAtom::Boolean(b) => b.serialize(serializer),
+ OvsdbAtom::Uuid(uuid) => {
+ let wrapper = ("uuid", uuid.to_string());
+ wrapper.serialize(serializer)
+ }
+ OvsdbAtom::NamedUuid(name) => {
+ let wrapper = ("named-uuid", name);
+ wrapper.serialize(serializer)
+ }
+ }
+ }
+}
+
+/// Extension trait for OvsdbSerializable to handle JSON conversion
+pub trait OvsdbSerializableExt: OvsdbSerializable {
+ fn to_ovsdb_json(&self) -> Option<serde_json::Value> {
+ serde_json::to_value(self.to_ovsdb()).ok()
+ }
+
+ fn from_ovsdb_json(json: &serde_json::Value) -> Option<Self> {
+ // Convert JSON to OvsdbValue
+ let value = json_to_ovsdb_value(json)?;
+ Self::from_ovsdb(&value)
+ }
+}
+
+// Implement the extension trait for all types that implement OvsdbSerializable
+impl<T: OvsdbSerializable> OvsdbSerializableExt for T {}
+
+/// Helper function to extract a UUID from a JSON value
+pub fn extract_uuid(value: &serde_json::Value) -> Option<Uuid> {
+ if let serde_json::Value::Array(arr) = value {
+ if arr.len() == 2 && arr[0] == "uuid" {
+ if let serde_json::Value::String(uuid_str) = &arr[1] {
+ return Uuid::parse_str(uuid_str).ok();
+ }
+ }
+ }
+ None
+}
+
+/// Convert a JSON value to an OvsdbValue
+fn json_to_ovsdb_value(json: &serde_json::Value) -> Option<OvsdbValue> {
+ match json {
+ serde_json::Value::String(s) => Some(OvsdbValue::Atom(OvsdbAtom::String(s.clone()))),
+ serde_json::Value::Number(n) => {
+ if let Some(i) = n.as_i64() {
+ Some(OvsdbValue::Atom(OvsdbAtom::Integer(i)))
+ } else {
+ n.as_f64().map(|f| OvsdbValue::Atom(OvsdbAtom::Real(f)))
+ }
+ }
+ serde_json::Value::Bool(b) => Some(OvsdbValue::Atom(OvsdbAtom::Boolean(*b))),
+ serde_json::Value::Array(arr) => {
+ if arr.len() == 2 {
+ if let serde_json::Value::String(tag) = &arr[0] {
+ match tag.as_str() {
+ "uuid" => {
+ if let serde_json::Value::String(uuid_str) = &arr[1] {
+ if let Ok(uuid) = Uuid::parse_str(uuid_str) {
+ return Some(OvsdbValue::Atom(OvsdbAtom::Uuid(uuid)));
+ }
+ }
+ }
+ "named-uuid" => {
+ if let serde_json::Value::String(name) = &arr[1] {
+ return Some(OvsdbValue::Atom(OvsdbAtom::NamedUuid(name.clone())));
+ }
+ }
+ "set" => {
+ if let serde_json::Value::Array(elements) = &arr[1] {
+ let mut atoms = Vec::with_capacity(elements.len());
+ for elem in elements {
+ if let Some(OvsdbValue::Atom(atom)) = json_to_ovsdb_value(elem)
+ {
+ atoms.push(atom);
+ } else {
+ return None;
+ }
+ }
+ return Some(OvsdbValue::Set(atoms));
+ }
+ }
+ "map" => {
+ if let serde_json::Value::Array(pairs) = &arr[1] {
+ let mut map_pairs = Vec::with_capacity(pairs.len());
+ for pair in pairs {
+ if let serde_json::Value::Array(kv) = pair {
+ if kv.len() == 2 {
+ if let (
+ Some(OvsdbValue::Atom(key)),
+ Some(OvsdbValue::Atom(value)),
+ ) = (
+ json_to_ovsdb_value(&kv[0]),
+ json_to_ovsdb_value(&kv[1]),
+ ) {
+ map_pairs.push((key, value));
+ continue;
+ }
+ }
+ }
+ return None;
+ }
+ return Some(OvsdbValue::Map(map_pairs));
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+ // Empty array means empty set
+ if arr.is_empty() {
+ return Some(OvsdbValue::Set(vec![]));
+ }
+
+ None
+ }
+ serde_json::Value::Null => {
+ // Null is represented as an empty set
+ Some(OvsdbValue::Set(vec![]))
+ }
+ _ => None,
+ }
+}