diff --git a/engine/packages/util-serde/Cargo.toml b/engine/packages/util-serde/Cargo.toml new file mode 100644 index 0000000000..f1a5153ba1 --- /dev/null +++ b/engine/packages/util-serde/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rivet-util-serde" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +indexmap.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true diff --git a/engine/packages/util-serde/src/lib.rs b/engine/packages/util-serde/src/lib.rs new file mode 100644 index 0000000000..9f01f8c1cd --- /dev/null +++ b/engine/packages/util-serde/src/lib.rs @@ -0,0 +1,517 @@ +use std::{ + collections::HashMap, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +use ::serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{self, DeserializeOwned}, +}; +use indexmap::IndexMap; +use serde_json::{Number, value::RawValue}; +use thiserror::Error; + +/// Like serde_json::Value but with no nesting types (arrays, objects). +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SimpleValue { + Null, + Bool(bool), + Number(Number), + String(String), +} + +#[derive(Debug, Error)] +pub enum DeserializeError { + #[error("arrays and objects are not supported")] + OnlySimple, +} + +impl<'de> Deserialize<'de> for SimpleValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SimpleValueVisitor; + + impl<'de> de::Visitor<'de> for SimpleValueVisitor { + type Value = SimpleValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a simple value (null, bool, number, or string)") + } + + fn visit_bool(self, value: bool) -> Result { + Ok(SimpleValue::Bool(value)) + } + + fn visit_i64(self, value: i64) -> Result { + Ok(SimpleValue::Number(value.into())) + } + + fn visit_u64(self, value: u64) -> Result { + Ok(SimpleValue::Number(value.into())) + } + + fn visit_f64(self, value: f64) -> Result { + Ok(Number::from_f64(value).map_or(SimpleValue::Null, SimpleValue::Number)) + } + + fn visit_str(self, value: &str) -> Result { + Ok(SimpleValue::String(value.to_owned())) + } + + fn visit_string(self, value: String) -> Result { + Ok(SimpleValue::String(value)) + } + + fn visit_none(self) -> Result { + Ok(SimpleValue::Null) + } + + fn visit_unit(self) -> Result { + Ok(SimpleValue::Null) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer) + } + + fn visit_map(self, _visitor: V) -> Result + where + V: de::MapAccess<'de>, + { + Err(de::Error::custom(DeserializeError::OnlySimple)) + } + + fn visit_seq(self, _visitor: V) -> Result + where + V: de::SeqAccess<'de>, + { + Err(de::Error::custom(DeserializeError::OnlySimple)) + } + } + + deserializer.deserialize_any(SimpleValueVisitor) + } +} + +impl Serialize for SimpleValue { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer, + { + match self { + SimpleValue::Null => serializer.serialize_unit(), + SimpleValue::Bool(b) => serializer.serialize_bool(*b), + SimpleValue::Number(n) => n.serialize(serializer), + SimpleValue::String(s) => serializer.serialize_str(s), + } + } +} + +/// Used in workflow activity inputs/outputs. Using this over BTreeMap is preferred because this does not +/// reorder keys, providing faster insert and lookup. +#[derive(::serde::Serialize, ::serde::Deserialize)] +pub struct HashableMap(IndexMap); + +impl HashableMap { + pub fn new() -> Self { + HashableMap(IndexMap::new()) + } + + pub fn with_capacity(capacity: usize) -> Self { + HashableMap(IndexMap::with_capacity(capacity)) + } +} + +impl Default for HashableMap { + fn default() -> Self { + HashableMap::new() + } +} + +impl PartialEq for HashableMap { + fn eq(&self, other: &HashableMap) -> bool { + if self.len() != other.len() { + return false; + } + + self.0 + .iter() + .all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) + } +} + +impl Eq for HashableMap {} + +impl Deref for HashableMap { + type Target = IndexMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for HashableMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Hash for HashableMap { + fn hash(&self, state: &mut H) { + let mut kv = Vec::from_iter(&self.0); + kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); + kv.hash(state); + } +} + +impl fmt::Debug for HashableMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl Clone for HashableMap { + fn clone(&self) -> Self { + HashableMap(self.0.clone()) + } + + fn clone_from(&mut self, other: &Self) { + self.0.clone_from(&other.0); + } +} + +pub trait AsHashableExt { + /// Converts the iterable to a `HashableMap` via cloning. + fn as_hashable(&self) -> HashableMap; +} + +impl AsHashableExt for HashMap { + fn as_hashable(&self) -> HashableMap { + HashableMap(self.iter().map(|(k, v)| (k.clone(), v.clone())).collect()) + } +} + +impl From> for HashableMap { + fn from(val: HashMap) -> Self { + HashableMap(val.into_iter().collect()) + } +} + +impl From> for HashMap { + fn from(val: HashableMap) -> Self { + val.into_iter().collect() + } +} + +impl FromIterator<(K, V)> for HashableMap { + fn from_iter>(iter: I) -> Self { + HashableMap(iter.into_iter().collect()) + } +} + +impl IntoIterator for HashableMap { + type Item = (K, V); + type IntoIter = indexmap::map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, K: Eq + Hash, V: Hash> IntoIterator for &'a HashableMap { + type Item = (&'a K, &'a V); + type IntoIter = indexmap::map::Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, K: Eq + Hash, V: Hash> IntoIterator for &'a mut HashableMap { + type Item = (&'a K, &'a mut V); + type IntoIter = indexmap::map::IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl Extend<(K, V)> for HashableMap { + fn extend>(&mut self, iter: T) { + self.0.extend(iter); + } +} + +// TODO: This doesn't work +// impl ToSchema for HashableMap { +// fn schemas( +// schemas: &mut Vec<( +// String, +// utoipa::openapi::RefOr, +// )>, +// ) { +// K::schemas(schemas); +// T::schemas(schemas); +// } +// } +// +// impl PartialSchema for HashableMap { +// fn schema() -> utoipa::openapi::RefOr { +// utoipa::openapi::ObjectBuilder::new() +// .additional_properties(Some(T::schema())) +// .into() +// } +// } + +/// Allows partial json ser/de. +/// Effectively a `serde_json::value::RawValue` with type information. +pub struct Raw { + _marker: PhantomData, + inner: Box, +} + +impl Raw { + pub fn from_string(s: String) -> Result { + Ok(Raw { + _marker: PhantomData, + inner: serde_json::value::RawValue::from_string(s)?, + }) + } +} + +impl Raw { + pub fn new(t: &T) -> Result { + Ok(Raw { + _marker: PhantomData, + inner: serde_json::value::to_raw_value(t)?, + }) + } +} + +impl Raw { + pub fn deserialize(&self) -> Result { + serde_json::from_str(self.inner.get()) + } +} + +impl Serialize for Raw { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.inner.serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for Raw +where + T: DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let inner = Box::::deserialize(deserializer)?; + Ok(Raw { + _marker: PhantomData, + inner, + }) + } +} + +impl Clone for Raw { + fn clone(&self) -> Self { + Raw { + _marker: PhantomData, + inner: self.inner.clone(), + } + } +} + +impl Hash for Raw { + fn hash(&self, state: &mut H) { + self.inner.get().hash(state); + } +} + +impl std::ops::Deref for Raw { + type Target = RawValue; + + fn deref(&self) -> &Self::Target { + self.inner.as_ref() + } +} + +impl std::fmt::Debug for Raw { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_tuple("Raw") + .field(&format_args!("{}", self.inner.get())) + .finish() + } +} + +/// A map-like structure that serializes/deserializes as a JSON object but is backed by a Vec. +/// Preserves insertion order and allows duplicate keys. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FakeMap(Vec<(K, V)>); + +impl FakeMap { + pub fn new() -> Self { + FakeMap(Vec::new()) + } + + pub fn with_capacity(capacity: usize) -> Self { + FakeMap(Vec::with_capacity(capacity)) + } +} + +impl FakeMap { + /// Sort by keys. + pub fn sort(&mut self) + where + K: Ord, + { + self.0.sort_by(|a, b| a.0.cmp(&b.0)); + } +} + +impl Default for FakeMap { + fn default() -> Self { + FakeMap::new() + } +} + +impl Deref for FakeMap { + type Target = Vec<(K, V)>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FakeMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for FakeMap { + fn from(val: Vec<(K, V)>) -> Self { + FakeMap(val) + } +} + +impl From> for Vec<(K, V)> { + fn from(val: FakeMap) -> Self { + val.0 + } +} + +impl From> for FakeMap { + fn from(val: HashMap) -> Self { + FakeMap(val.into_iter().collect()) + } +} + +impl From> for HashMap { + fn from(val: FakeMap) -> Self { + val.into_iter().collect() + } +} + +impl Serialize for FakeMap { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use ::serde::ser::SerializeMap; + + let mut map = serializer.serialize_map(Some(self.0.len()))?; + for (k, v) in &self.0 { + map.serialize_entry(k, v)?; + } + map.end() + } +} + +impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> Deserialize<'de> for FakeMap { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FakeMapVisitor { + marker: PhantomData<(K, V)>, + } + + impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> de::Visitor<'de> for FakeMapVisitor { + type Value = FakeMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map") + } + + fn visit_map(self, mut access: M) -> Result + where + M: de::MapAccess<'de>, + { + let mut vec = Vec::with_capacity(access.size_hint().unwrap_or(0)); + while let Some((key, value)) = access.next_entry()? { + vec.push((key, value)); + } + Ok(FakeMap(vec)) + } + } + + deserializer.deserialize_map(FakeMapVisitor { + marker: PhantomData, + }) + } +} + +impl FromIterator<(K, V)> for FakeMap { + fn from_iter>(iter: I) -> Self { + FakeMap(iter.into_iter().collect()) + } +} + +impl IntoIterator for FakeMap { + type Item = (K, V); + type IntoIter = std::vec::IntoIter<(K, V)>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, K, V> IntoIterator for &'a FakeMap { + type Item = &'a (K, V); + type IntoIter = std::slice::Iter<'a, (K, V)>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, K, V> IntoIterator for &'a mut FakeMap { + type Item = &'a mut (K, V); + type IntoIter = std::slice::IterMut<'a, (K, V)>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl Extend<(K, V)> for FakeMap { + fn extend>(&mut self, iter: T) { + self.0.extend(iter); + } +} diff --git a/engine/packages/util/Cargo.toml b/engine/packages/util/Cargo.toml index 32e55dcaaa..f45e143bb2 100644 --- a/engine/packages/util/Cargo.toml +++ b/engine/packages/util/Cargo.toml @@ -16,7 +16,6 @@ axum.workspace = true bcrypt.workspace = true chrono.workspace = true futures-util.workspace = true -indexmap.workspace = true ipnet.workspace = true lazy_static.workspace = true rand.workspace = true @@ -25,10 +24,9 @@ reqwest.workspace = true rivet-config.workspace = true rivet-metrics.workspace = true rivet-tracing-utils.workspace = true +rivet-util-serde.workspace = true rivet-util-id.workspace = true serde.workspace = true -serde_json.workspace = true -thiserror.workspace = true tokio.workspace = true tracing.workspace = true url.workspace = true diff --git a/engine/packages/util/src/serde.rs b/engine/packages/util/src/serde.rs index b2ae867888..20c97419cb 100644 --- a/engine/packages/util/src/serde.rs +++ b/engine/packages/util/src/serde.rs @@ -1,516 +1 @@ -use std::{ - collections::HashMap, - fmt, - hash::{Hash, Hasher}, - marker::PhantomData, - ops::{Deref, DerefMut}, -}; - -use indexmap::IndexMap; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{self, DeserializeOwned}, -}; -use serde_json::{Number, value::RawValue}; -use thiserror::Error; - -/// Like serde_json::Value but with no nesting types (arrays, objects). -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SimpleValue { - Null, - Bool(bool), - Number(Number), - String(String), -} - -#[derive(Debug, Error)] -pub enum DeserializeError { - #[error("arrays and objects are not supported")] - OnlySimple, -} - -impl<'de> Deserialize<'de> for SimpleValue { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct SimpleValueVisitor; - - impl<'de> de::Visitor<'de> for SimpleValueVisitor { - type Value = SimpleValue; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a simple value (null, bool, number, or string)") - } - - fn visit_bool(self, value: bool) -> Result { - Ok(SimpleValue::Bool(value)) - } - - fn visit_i64(self, value: i64) -> Result { - Ok(SimpleValue::Number(value.into())) - } - - fn visit_u64(self, value: u64) -> Result { - Ok(SimpleValue::Number(value.into())) - } - - fn visit_f64(self, value: f64) -> Result { - Ok(Number::from_f64(value).map_or(SimpleValue::Null, SimpleValue::Number)) - } - - fn visit_str(self, value: &str) -> Result { - Ok(SimpleValue::String(value.to_owned())) - } - - fn visit_string(self, value: String) -> Result { - Ok(SimpleValue::String(value)) - } - - fn visit_none(self) -> Result { - Ok(SimpleValue::Null) - } - - fn visit_unit(self) -> Result { - Ok(SimpleValue::Null) - } - - fn visit_some(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Deserialize::deserialize(deserializer) - } - - fn visit_map(self, _visitor: V) -> Result - where - V: de::MapAccess<'de>, - { - Err(de::Error::custom(DeserializeError::OnlySimple)) - } - - fn visit_seq(self, _visitor: V) -> Result - where - V: de::SeqAccess<'de>, - { - Err(de::Error::custom(DeserializeError::OnlySimple)) - } - } - - deserializer.deserialize_any(SimpleValueVisitor) - } -} - -impl Serialize for SimpleValue { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: ::serde::Serializer, - { - match self { - SimpleValue::Null => serializer.serialize_unit(), - SimpleValue::Bool(b) => serializer.serialize_bool(*b), - SimpleValue::Number(n) => n.serialize(serializer), - SimpleValue::String(s) => serializer.serialize_str(s), - } - } -} - -/// Used in workflow activity inputs/outputs. Using this over BTreeMap is preferred because this does not -/// reorder keys, providing faster insert and lookup. -#[derive(Serialize, Deserialize)] -pub struct HashableMap(IndexMap); - -impl HashableMap { - pub fn new() -> Self { - HashableMap(IndexMap::new()) - } - - pub fn with_capacity(capacity: usize) -> Self { - HashableMap(IndexMap::with_capacity(capacity)) - } -} - -impl Default for HashableMap { - fn default() -> Self { - HashableMap::new() - } -} - -impl PartialEq for HashableMap { - fn eq(&self, other: &HashableMap) -> bool { - if self.len() != other.len() { - return false; - } - - self.0 - .iter() - .all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) - } -} - -impl Eq for HashableMap {} - -impl Deref for HashableMap { - type Target = IndexMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for HashableMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Hash for HashableMap { - fn hash(&self, state: &mut H) { - let mut kv = Vec::from_iter(&self.0); - kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); - kv.hash(state); - } -} - -impl fmt::Debug for HashableMap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -impl Clone for HashableMap { - fn clone(&self) -> Self { - HashableMap(self.0.clone()) - } - - fn clone_from(&mut self, other: &Self) { - self.0.clone_from(&other.0); - } -} - -pub trait AsHashableExt { - /// Converts the iterable to a `HashableMap` via cloning. - fn as_hashable(&self) -> HashableMap; -} - -impl AsHashableExt for HashMap { - fn as_hashable(&self) -> HashableMap { - HashableMap(self.iter().map(|(k, v)| (k.clone(), v.clone())).collect()) - } -} - -impl From> for HashableMap { - fn from(val: HashMap) -> Self { - HashableMap(val.into_iter().collect()) - } -} - -impl From> for HashMap { - fn from(val: HashableMap) -> Self { - val.into_iter().collect() - } -} - -impl FromIterator<(K, V)> for HashableMap { - fn from_iter>(iter: I) -> Self { - HashableMap(iter.into_iter().collect()) - } -} - -impl IntoIterator for HashableMap { - type Item = (K, V); - type IntoIter = indexmap::map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, K: Eq + Hash, V: Hash> IntoIterator for &'a HashableMap { - type Item = (&'a K, &'a V); - type IntoIter = indexmap::map::Iter<'a, K, V>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, K: Eq + Hash, V: Hash> IntoIterator for &'a mut HashableMap { - type Item = (&'a K, &'a mut V); - type IntoIter = indexmap::map::IterMut<'a, K, V>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl Extend<(K, V)> for HashableMap { - fn extend>(&mut self, iter: T) { - self.0.extend(iter); - } -} - -// TODO: This doesn't work -// impl ToSchema for HashableMap { -// fn schemas( -// schemas: &mut Vec<( -// String, -// utoipa::openapi::RefOr, -// )>, -// ) { -// K::schemas(schemas); -// T::schemas(schemas); -// } -// } -// -// impl PartialSchema for HashableMap { -// fn schema() -> utoipa::openapi::RefOr { -// utoipa::openapi::ObjectBuilder::new() -// .additional_properties(Some(T::schema())) -// .into() -// } -// } - -/// Allows partial json ser/de. -/// Effectively a `serde_json::value::RawValue` with type information. -pub struct Raw { - _marker: PhantomData, - inner: Box, -} - -impl Raw { - pub fn from_string(s: String) -> Result { - Ok(Raw { - _marker: PhantomData, - inner: serde_json::value::RawValue::from_string(s)?, - }) - } -} - -impl Raw { - pub fn new(t: &T) -> Result { - Ok(Raw { - _marker: PhantomData, - inner: serde_json::value::to_raw_value(t)?, - }) - } -} - -impl Raw { - pub fn deserialize(&self) -> Result { - serde_json::from_str(self.inner.get()) - } -} - -impl Serialize for Raw { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.inner.serialize(serializer) - } -} - -impl<'de, T> Deserialize<'de> for Raw -where - T: DeserializeOwned, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let inner = Box::::deserialize(deserializer)?; - Ok(Raw { - _marker: PhantomData, - inner, - }) - } -} - -impl Clone for Raw { - fn clone(&self) -> Self { - Raw { - _marker: PhantomData, - inner: self.inner.clone(), - } - } -} - -impl Hash for Raw { - fn hash(&self, state: &mut H) { - self.inner.get().hash(state); - } -} - -impl std::ops::Deref for Raw { - type Target = RawValue; - - fn deref(&self) -> &Self::Target { - self.inner.as_ref() - } -} - -impl std::fmt::Debug for Raw { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_tuple("Raw") - .field(&format_args!("{}", self.inner.get())) - .finish() - } -} - -/// A map-like structure that serializes/deserializes as a JSON object but is backed by a Vec. -/// Preserves insertion order and allows duplicate keys. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FakeMap(Vec<(K, V)>); - -impl FakeMap { - pub fn new() -> Self { - FakeMap(Vec::new()) - } - - pub fn with_capacity(capacity: usize) -> Self { - FakeMap(Vec::with_capacity(capacity)) - } -} - -impl FakeMap { - /// Sort by keys. - pub fn sort(&mut self) - where - K: Ord, - { - self.0.sort_by(|a, b| a.0.cmp(&b.0)); - } -} - -impl Default for FakeMap { - fn default() -> Self { - FakeMap::new() - } -} - -impl Deref for FakeMap { - type Target = Vec<(K, V)>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for FakeMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl From> for FakeMap { - fn from(val: Vec<(K, V)>) -> Self { - FakeMap(val) - } -} - -impl From> for Vec<(K, V)> { - fn from(val: FakeMap) -> Self { - val.0 - } -} - -impl From> for FakeMap { - fn from(val: HashMap) -> Self { - FakeMap(val.into_iter().collect()) - } -} - -impl From> for HashMap { - fn from(val: FakeMap) -> Self { - val.into_iter().collect() - } -} - -impl Serialize for FakeMap { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeMap; - let mut map = serializer.serialize_map(Some(self.0.len()))?; - for (k, v) in &self.0 { - map.serialize_entry(k, v)?; - } - map.end() - } -} - -impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> Deserialize<'de> for FakeMap { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FakeMapVisitor { - marker: PhantomData<(K, V)>, - } - - impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> de::Visitor<'de> for FakeMapVisitor { - type Value = FakeMap; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map") - } - - fn visit_map(self, mut access: M) -> Result - where - M: de::MapAccess<'de>, - { - let mut vec = Vec::with_capacity(access.size_hint().unwrap_or(0)); - while let Some((key, value)) = access.next_entry()? { - vec.push((key, value)); - } - Ok(FakeMap(vec)) - } - } - - deserializer.deserialize_map(FakeMapVisitor { - marker: PhantomData, - }) - } -} - -impl FromIterator<(K, V)> for FakeMap { - fn from_iter>(iter: I) -> Self { - FakeMap(iter.into_iter().collect()) - } -} - -impl IntoIterator for FakeMap { - type Item = (K, V); - type IntoIter = std::vec::IntoIter<(K, V)>; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, K, V> IntoIterator for &'a FakeMap { - type Item = &'a (K, V); - type IntoIter = std::slice::Iter<'a, (K, V)>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a, K, V> IntoIterator for &'a mut FakeMap { - type Item = &'a mut (K, V); - type IntoIter = std::slice::IterMut<'a, (K, V)>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl Extend<(K, V)> for FakeMap { - fn extend>(&mut self, iter: T) { - self.0.extend(iter); - } -} +pub use rivet_util_serde::*; diff --git a/engine/sdks/rust/envoy-client/Cargo.toml b/engine/sdks/rust/envoy-client/Cargo.toml index 5c29871800..8f9fde7136 100644 --- a/engine/sdks/rust/envoy-client/Cargo.toml +++ b/engine/sdks/rust/envoy-client/Cargo.toml @@ -11,7 +11,7 @@ futures-util.workspace = true hex.workspace = true rand.workspace = true rivet-envoy-protocol.workspace = true -rivet-util.workspace = true +rivet-util-serde.workspace = true scc.workspace = true serde.workspace = true serde_bare.workspace = true diff --git a/engine/sdks/rust/envoy-client/src/actor.rs b/engine/sdks/rust/envoy-client/src/actor.rs index 01538a52f0..fc040b8382 100644 --- a/engine/sdks/rust/envoy-client/src/actor.rs +++ b/engine/sdks/rust/envoy-client/src/actor.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::sync::Arc; use rivet_envoy_protocol as protocol; -use rivet_util::serde::HashableMap; +use rivet_util_serde::HashableMap; use tokio::sync::mpsc; use crate::config::{HttpRequest, HttpResponse, WebSocketMessage}; diff --git a/engine/sdks/rust/envoy-client/src/connection.rs b/engine/sdks/rust/envoy-client/src/connection.rs index 1e00b3cca4..0a41bdc4f9 100644 --- a/engine/sdks/rust/envoy-client/src/connection.rs +++ b/engine/sdks/rust/envoy-client/src/connection.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use futures_util::{SinkExt, StreamExt}; use rivet_envoy_protocol as protocol; -use rivet_util::serde::HashableMap; +use rivet_util_serde::HashableMap; use tokio::sync::mpsc; use tokio_tungstenite::tungstenite; use vbare::OwnedVersionedData; diff --git a/engine/sdks/rust/envoy-client/src/stringify.rs b/engine/sdks/rust/envoy-client/src/stringify.rs index b20821682e..51bae8d436 100644 --- a/engine/sdks/rust/envoy-client/src/stringify.rs +++ b/engine/sdks/rust/envoy-client/src/stringify.rs @@ -1,5 +1,5 @@ use rivet_envoy_protocol as protocol; -use rivet_util::serde::HashableMap; +use rivet_util_serde::HashableMap; use crate::utils::id_to_str; diff --git a/engine/sdks/rust/envoy-client/src/tunnel.rs b/engine/sdks/rust/envoy-client/src/tunnel.rs index 1405bc343f..2ea6ef4714 100644 --- a/engine/sdks/rust/envoy-client/src/tunnel.rs +++ b/engine/sdks/rust/envoy-client/src/tunnel.rs @@ -236,7 +236,7 @@ async fn send_error_response( request_id: protocol::RequestId, ) { let body = b"Actor not found".to_vec(); - let mut headers = rivet_util::serde::HashableMap::new(); + let mut headers = rivet_util_serde::HashableMap::new(); headers.insert( "x-rivet-error".to_string(), "envoy.actor_not_found".to_string(), diff --git a/engine/sdks/rust/envoy-protocol/Cargo.toml b/engine/sdks/rust/envoy-protocol/Cargo.toml index f2137b94c8..c4ee33a78b 100644 --- a/engine/sdks/rust/envoy-protocol/Cargo.toml +++ b/engine/sdks/rust/envoy-protocol/Cargo.toml @@ -9,7 +9,7 @@ edition.workspace = true anyhow.workspace = true hex.workspace = true rand.workspace = true -rivet-util.workspace = true +rivet-util-serde.workspace = true serde_bare.workspace = true serde.workspace = true utoipa.workspace = true diff --git a/engine/sdks/rust/envoy-protocol/build.rs b/engine/sdks/rust/envoy-protocol/build.rs index fe9c328905..ce445e61dc 100644 --- a/engine/sdks/rust/envoy-protocol/build.rs +++ b/engine/sdks/rust/envoy-protocol/build.rs @@ -6,6 +6,7 @@ use std::{ fn main() -> Result<(), Box> { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; + let out_dir = PathBuf::from(std::env::var("OUT_DIR")?); let workspace_root = Path::new(&manifest_dir) .parent() .and_then(|p| p.parent()) @@ -20,6 +21,7 @@ fn main() -> Result<(), Box> { // Rust SDK generation let cfg = vbare_compiler::Config::with_hashable_map(); vbare_compiler::process_schemas_with_config(&schema_dir, &cfg)?; + post_process_generated_rust(&out_dir)?; // TypeScript SDK generation let cli_js_path = workspace_root @@ -38,6 +40,34 @@ fn main() -> Result<(), Box> { Ok(()) } +fn post_process_generated_rust(out_dir: &Path) -> Result<(), Box> { + for entry in fs::read_dir(out_dir)?.flatten() { + let path = entry.path(); + if path.extension().and_then(|ext| ext.to_str()) != Some("rs") { + continue; + } + + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + if !file_name.ends_with("_generated.rs") { + continue; + } + + let content = fs::read_to_string(&path)?; + let rewritten = content.replace( + "rivet_util::serde::HashableMap", + "rivet_util_serde::HashableMap", + ); + + if rewritten != content { + fs::write(path, rewritten)?; + } + } + + Ok(()) +} + mod typescript { use super::*;