From 35f85695bae8dd39ce05e6f6b2fe4d1fff22cc13 Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 20 Mar 2024 05:42:35 +0100 Subject: [PATCH] feat: macros for getters/setters, refactored --- src/activitystream/macros.rs | 339 +++++++++++++++++++++++++++++++++++ src/activitystream/node.rs | 45 ----- src/activitystream/types.rs | 90 ---------- 3 files changed, 339 insertions(+), 135 deletions(-) create mode 100644 src/activitystream/macros.rs delete mode 100644 src/activitystream/types.rs diff --git a/src/activitystream/macros.rs b/src/activitystream/macros.rs new file mode 100644 index 0000000..93621eb --- /dev/null +++ b/src/activitystream/macros.rs @@ -0,0 +1,339 @@ +use super::Node; + +#[derive(Debug, thiserror::Error)] +#[error("invalid type value")] +pub struct TypeValueError; + +impl From for sea_orm::sea_query::ValueTypeErr { + fn from(_: TypeValueError) -> Self { + sea_orm::sea_query::ValueTypeErr + } +} + +impl From for sea_orm::TryGetError { + fn from(_: TypeValueError) -> Self { + sea_orm::TryGetError::Null("value is not a valid type".into()) + } +} + + +#[macro_export] +macro_rules! strenum { + ( $(pub enum $enum_name:ident { $($flat:ident),* ; $($deep:ident($inner:ident)),* };)+ ) => { + $( + #[derive(PartialEq, Eq, Debug, Clone, Copy)] + pub enum $enum_name { + $($flat,)* + $($deep($inner),)* + } + + impl AsRef for $enum_name { + fn as_ref(&self) -> &str { + match self { + $(Self::$flat => stringify!($flat),)* + $(Self::$deep(x) => x.as_ref(),)* + } + } + } + + impl TryFrom<&str> for $enum_name { + type Error = $crate::activitystream::macros::TypeValueError; + + fn try_from(value:&str) -> Result { + match value { + $(stringify!($flat) => Ok(Self::$flat),)* + _ => { + $( + if let Ok(x) = $inner::try_from(value) { + return Ok(Self::$deep(x)); + } + )* + Err($crate::activitystream::macros::TypeValueError) + }, + } + } + } + + impl From<$enum_name> for sea_orm::Value { + fn from(value: $enum_name) -> sea_orm::Value { + sea_orm::Value::String(Some(Box::new(value.as_ref().to_string()))) + } + } + + impl sea_orm::sea_query::ValueType for $enum_name { + fn try_from(v: sea_orm::Value) -> Result { + match v { + sea_orm::Value::String(Some(x)) => + Ok(>::try_from(x.as_str())?), + _ => Err(sea_orm::sea_query::ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!($enum_name).to_string() + } + + fn array_type() -> sea_orm::sea_query::ArrayType { + sea_orm::sea_query::ArrayType::String + } + + fn column_type() -> sea_orm::sea_query::ColumnType { + sea_orm::sea_query::ColumnType::String(Some(24)) + } + } + + impl sea_orm::TryGetable for $enum_name { + fn try_get_by(res: &sea_orm::prelude::QueryResult, index: I) -> Result { + let x : String = res.try_get_by(index)?; + Ok(Self::try_from(x.as_str())?) + } + } + )* + }; +} + +#[macro_export] +macro_rules! getter { + ($name:ident -> type $t:ty) => { + fn $name(&self) -> Option<$t> { + self.get("type")?.as_str()?.try_into().ok() + } + }; + + ($name:ident -> &str) => { + fn $name(&self) -> Option<&str> { + self.get(stringify!($name))?.as_str() + } + }; + + ($name:ident::$rename:ident -> &str) => { + fn $name(&self) -> Option<&str> { + self.get(stringify!($rename))?.as_str() + } + }; + + ($name:ident -> f64) => { + fn $name(&self) -> Option { + self.get(stringify!($name))?.as_f64() + } + }; + + ($name:ident::$rename:ident -> f64) => { + fn $name(&self) -> Option { + self.get(stringify!($rename))?.as_f64() + } + }; + + ($name:ident -> chrono::DateTime) => { + fn $name(&self) -> Option> { + Some( + chrono::DateTime::parse_from_rfc3339( + self + .get(stringify!($name))? + .as_str()? + ) + .ok()? + .with_timezone(&chrono::Utc) + ) + } + }; + + ($name:ident::$rename:ident -> chrono::DateTime) => { + fn $name(&self) -> Option> { + Some( + chrono::DateTime::parse_from_rfc3339( + self + .get(stringify!($rename))? + .as_str()? + ) + .ok()? + .with_timezone(&chrono::Utc) + ) + } + }; + + ($name:ident -> node $t:ty) => { + fn $name(&self) -> $crate::activitystream::Node<$t> { + match self.get(stringify!($name)) { + Some(x) => $crate::activitystream::Node::from(x.clone()), + None => $crate::activitystream::Node::Empty, + } + } + }; + + ($name:ident::$rename:ident -> node $t:ty) => { + fn $name(&self) -> $crate::activitystream::Node<$t> { + match self.get(stringify!($rename)) { + Some(x) => $crate::activitystream::Node::from(x.clone()), + None => $crate::activitystream::Node::Empty, + } + } + }; +} + +#[macro_export] +macro_rules! setter { + ($name:ident -> &str) => { + paste::item! { + fn [< set_$name >](&mut self, val: Option<&str>) -> &mut Self { + $crate::activitystream::macros::set_maybe_value( + self, stringify!($name), val.map(|x| serde_json::Value::String(x.to_string())) + ); + self + } + } + }; + + ($name:ident -> chrono::DateTime) => { + paste::item! { + fn [< set_$name >](&mut self, val: Option>) -> &mut Self { + $crate::activitystream::macros::set_maybe_value( + self, stringify!($name), val.map(|x| serde_json::Value::String(x.to_rfc3339())) + ); + self + } + } + }; + + ($name:ident::$rename:ident -> chrono::DateTime) => { + paste::item! { + fn [< set_$name >](&mut self, val: Option>) -> &mut Self { + $crate::activitystream::macros::set_maybe_value( + self, stringify!($rename), val.map(|x| serde_json::Value::String(x.to_rfc3339())) + ); + self + } + } + }; + + ($name:ident -> node $t:ty ) => { + paste::item! { + fn [< set_$name >](&mut self, val: $crate::activitystream::Node<$t>) -> &mut Self { + $crate::activitystream::macros::set_maybe_node( + self, stringify!($name), val + ); + self + } + } + }; + + ($name:ident::$rename:ident -> node $t:ty ) => { + paste::item! { + fn [< set_$name >](&mut self, val: $crate::activitystream::Node<$t>) -> &mut Self { + $crate::activitystream::macros::set_maybe_node( + self, stringify!($rename), val + ); + self + } + } + }; + + ($name:ident -> type $t:ty ) => { + paste::item! { + fn [< set_$name >](&mut self, val: Option<$t>) -> &mut Self { + $crate::activitystream::macros::set_maybe_value( + self, "type", val.map(|x| serde_json::Value::String(x.as_ref().to_string())) + ); + self + } + } + }; +} + +pub fn set_maybe_node(obj: &mut serde_json::Value, key: &str, node: super::Node) { + match node { + super::Node::Object(x) => { + set_maybe_value( + obj, key, Some(x.underlying_json_object()), + ); + }, + super::Node::Link(l) => { + set_maybe_value( + obj, key, Some(serde_json::Value::String(l.href().to_string())), + ); + }, + super::Node::Array(_) => { + set_maybe_value( + obj, key, Some(serde_json::Value::Array(node.flat())), + ); + }, + super::Node::Empty => { + set_maybe_value( + obj, key, None, + ); + }, + } +} + +pub fn set_maybe_value(obj: &mut serde_json::Value, key: &str, value: Option) { + if let Some(map) = obj.as_object_mut() { + match value { + Some(x) => map.insert(key.to_string(), x), + None => map.remove(key), + }; + } else { + tracing::error!("error setting '{key}' on json Value: not an object"); + } +} + +pub(crate) trait InsertValue { + fn insert_node(&mut self, k: &str, v: Node); + fn insert_str(&mut self, k: &str, v: Option<&str>); + fn insert_float(&mut self, k: &str, f: Option); + fn insert_timestr(&mut self, k: &str, t: Option>); +} + +impl InsertValue for serde_json::Map { + fn insert_node(&mut self, k: &str, node: Node) { + match node { + Node::Object(x) => { + self.insert( + k.to_string(), + *x, + ); + }, + Node::Array(ref _arr) => { + self.insert( + k.to_string(), + serde_json::Value::Array(node.flat()), + ); + }, + Node::Link(l) => { + self.insert( + k.to_string(), + serde_json::Value::String(l.href().to_string()), + ); + }, + Node::Empty => {}, + }; + } + + fn insert_str(&mut self, k: &str, v: Option<&str>) { + if let Some(v) = v { + self.insert( + k.to_string(), + serde_json::Value::String(v.to_string()), + ); + } + } + + fn insert_float(&mut self, k: &str, v: Option) { + if let Some(v) = v { + if let Some(n) = serde_json::Number::from_f64(v) { + self.insert( + k.to_string(), + serde_json::Value::Number(n), + ); + } + } + } + + fn insert_timestr(&mut self, k: &str, t: Option>) { + if let Some(published) = t { + self.insert( + k.to_string(), + serde_json::Value::String(published.to_rfc3339()), + ); + } + } +} diff --git a/src/activitystream/node.rs b/src/activitystream/node.rs index 185ca8a..b288a8c 100644 --- a/src/activitystream/node.rs +++ b/src/activitystream/node.rs @@ -129,48 +129,3 @@ impl Node{ -pub(crate) trait NodeExtractor { - fn node(&self, id: &str) -> Node; - fn node_vec(&self, id: &str) -> Node; -} - -impl NodeExtractor for serde_json::Value { - fn node(&self, id: &str) -> Node { - match self.get(id) { - None => Node::Empty, - Some(x) => Node::from(x.clone()), - } - } - - fn node_vec(&self, id: &str) -> Node { - match self.get(id) { - None => Node::Empty, - Some(x) => Node::from(x.clone()), - } - } -} - -pub(crate) trait InsertStr { - fn insert_str(&mut self, k: &str, v: Option<&str>); - fn insert_timestr(&mut self, k: &str, t: Option>); -} - -impl InsertStr for serde_json::Map { - fn insert_str(&mut self, k: &str, v: Option<&str>) { - if let Some(v) = v { - self.insert( - k.to_string(), - serde_json::Value::String(v.to_string()), - ); - } - } - - fn insert_timestr(&mut self, k: &str, t: Option>) { - if let Some(published) = t { - self.insert( - k.to_string(), - serde_json::Value::String(published.to_rfc3339()), - ); - } - } -} diff --git a/src/activitystream/types.rs b/src/activitystream/types.rs deleted file mode 100644 index b90e3c1..0000000 --- a/src/activitystream/types.rs +++ /dev/null @@ -1,90 +0,0 @@ -#[derive(Debug, thiserror::Error)] -#[error("invalid type value")] -pub struct TypeValueError; - -impl From for sea_orm::sea_query::ValueTypeErr { - fn from(_: TypeValueError) -> Self { - sea_orm::sea_query::ValueTypeErr - } -} - -impl From for sea_orm::TryGetError { - fn from(_: TypeValueError) -> Self { - sea_orm::TryGetError::Null("value is not a valid type".into()) - } -} - -#[macro_export] -macro_rules! strenum { - ( $(pub enum $enum_name:ident { $($flat:ident),* ; $($deep:ident($inner:ident)),* };)+ ) => { - $( - #[derive(PartialEq, Eq, Debug, Clone, Copy)] - pub enum $enum_name { - $($flat,)* - $($deep($inner),)* - } - - impl AsRef for $enum_name { - fn as_ref(&self) -> &str { - match self { - $(Self::$flat => stringify!($flat),)* - $(Self::$deep(x) => x.as_ref(),)* - } - } - } - - impl TryFrom<&str> for $enum_name { - type Error = $crate::activitystream::types::TypeValueError; - - fn try_from(value:&str) -> Result { - match value { - $(stringify!($flat) => Ok(Self::$flat),)* - _ => { - $( - if let Ok(x) = $inner::try_from(value) { - return Ok(Self::$deep(x)); - } - )* - Err($crate::activitystream::types::TypeValueError) - }, - } - } - } - - impl From<$enum_name> for sea_orm::Value { - fn from(value: $enum_name) -> sea_orm::Value { - sea_orm::Value::String(Some(Box::new(value.as_ref().to_string()))) - } - } - - impl sea_orm::sea_query::ValueType for $enum_name { - fn try_from(v: sea_orm::Value) -> Result { - match v { - sea_orm::Value::String(Some(x)) => - Ok(>::try_from(x.as_str())?), - _ => Err(sea_orm::sea_query::ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($enum_name).to_string() - } - - fn array_type() -> sea_orm::sea_query::ArrayType { - sea_orm::sea_query::ArrayType::String - } - - fn column_type() -> sea_orm::sea_query::ColumnType { - sea_orm::sea_query::ColumnType::String(Some(24)) - } - } - - impl sea_orm::TryGetable for $enum_name { - fn try_get_by(res: &sea_orm::prelude::QueryResult, index: I) -> Result { - let x : String = res.try_get_by(index)?; - Ok(Self::try_from(x.as_str())?) - } - } - )* - }; -}