From a9504de10db7c6434d89588a71c86d99b9472be4 Mon Sep 17 00:00:00 2001 From: silverpill Date: Thu, 21 Apr 2022 22:37:08 +0000 Subject: [PATCH] Add json_from_sql and json_to_sql macros --- src/activitypub/actor.rs | 2 +- src/database/int_enum.rs | 3 ++- src/database/json_macro.rs | 39 +++++++++++++++++++++++++++++ src/database/mod.rs | 1 + src/mastodon_api/accounts/types.rs | 4 +-- src/models/profiles/types.rs | 40 +++++------------------------- 6 files changed, 51 insertions(+), 38 deletions(-) create mode 100644 src/database/json_macro.rs diff --git a/src/activitypub/actor.rs b/src/activitypub/actor.rs index ccd54b8..f286d2e 100644 --- a/src/activitypub/actor.rs +++ b/src/activitypub/actor.rs @@ -184,7 +184,7 @@ pub fn get_local_actor( None => None, }; let properties = user.profile.extra_fields.clone() - .unpack().into_iter() + .into_inner().into_iter() .map(|field| { ActorProperty { object_type: PROPERTY_VALUE.to_string(), diff --git a/src/database/int_enum.rs b/src/database/int_enum.rs index c2e063e..5d4388a 100644 --- a/src/database/int_enum.rs +++ b/src/database/int_enum.rs @@ -19,7 +19,8 @@ macro_rules! int_enum_to_sql { ($t:ty) => { impl postgres_types::ToSql for $t { fn to_sql( - &self, _: &postgres_types::Type, + &self, + _: &postgres_types::Type, out: &mut postgres_types::private::BytesMut, ) -> Result> { let int_value: i16 = self.into(); diff --git a/src/database/json_macro.rs b/src/database/json_macro.rs new file mode 100644 index 0000000..c784771 --- /dev/null +++ b/src/database/json_macro.rs @@ -0,0 +1,39 @@ +/// Implements FromSql trait for any de-serializable type +macro_rules! json_from_sql { + ($t:ty) => { + impl<'a> postgres_types::FromSql<'a> for $t { + fn from_sql( + ty: &postgres_types::Type, + raw: &'a [u8], + ) -> Result<$t, Box> { + let postgres_types::Json(json_value) = + postgres_types::Json::::from_sql(ty, raw)?; + let value: $t = serde_json::from_value(json_value)?; + Ok(value) + } + + postgres_types::accepts!(JSON, JSONB); + } + } +} + +/// Implements ToSql trait for any serializable type +macro_rules! json_to_sql { + ($t:ty) => { + impl postgres_types::ToSql for $t { + fn to_sql( + &self, + ty: &postgres_types::Type, + out: &mut postgres_types::private::BytesMut, + ) -> Result> { + let value = serde_json::to_value(self)?; + postgres_types::Json(value).to_sql(ty, out) + } + + postgres_types::accepts!(JSON, JSONB); + postgres_types::to_sql_checked!(); + } + } +} + +pub(crate) use {json_from_sql, json_to_sql}; diff --git a/src/database/mod.rs b/src/database/mod.rs index 0010d30..b9276fa 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -3,6 +3,7 @@ use tokio_postgres::error::{Error as PgError, SqlState}; use crate::errors::DatabaseError; pub mod int_enum; +pub mod json_macro; pub mod migrate; pub mod query_macro; diff --git a/src/mastodon_api/accounts/types.rs b/src/mastodon_api/accounts/types.rs index f07139e..b847691 100644 --- a/src/mastodon_api/accounts/types.rs +++ b/src/mastodon_api/accounts/types.rs @@ -59,7 +59,7 @@ impl Account { .map(|name| get_file_url(instance_url, name)); let header_url = profile.banner_file_name.as_ref() .map(|name| get_file_url(instance_url, name)); - let fields = profile.extra_fields.unpack().into_iter() + let fields = profile.extra_fields.into_inner().into_iter() .map(|field| AccountField { name: field.name, value: field.value }) .collect(); Self { @@ -83,7 +83,7 @@ impl Account { pub fn from_user(user: User, instance_url: &str) -> Self { let fields_sources = user.profile.extra_fields.clone() - .unpack().into_iter() + .into_inner().into_iter() .map(|field| AccountField { name: field.name, value: field.value_source.unwrap_or(field.value), diff --git a/src/models/profiles/types.rs b/src/models/profiles/types.rs index 3e8a3f8..7728541 100644 --- a/src/models/profiles/types.rs +++ b/src/models/profiles/types.rs @@ -1,15 +1,12 @@ use chrono::{DateTime, Utc}; -use postgres_types::{ - FromSql, ToSql, IsNull, Type, Json, - accepts, to_sql_checked, - private::BytesMut, -}; +use postgres_types::FromSql; use serde::{Deserialize, Serialize}; use serde_json::Value; use uuid::Uuid; use crate::activitypub::actor::Actor; use crate::activitypub::views::get_actor_url; +use crate::database::json_macro::{json_from_sql, json_to_sql}; use crate::errors::ValidationError; use super::validators::{ validate_username, @@ -29,7 +26,7 @@ pub struct ExtraField { pub struct ExtraFields(pub Vec); impl ExtraFields { - pub fn unpack(self) -> Vec { + pub fn into_inner(self) -> Vec { let Self(extra_fields) = self; extra_fields } @@ -39,35 +36,10 @@ pub fn get_currency_field_name(currency_code: &str) -> String { format!("${}", currency_code.to_uppercase()) } -type SqlError = Box; +json_from_sql!(ExtraFields); +json_to_sql!(ExtraFields); -impl<'a> FromSql<'a> for ExtraFields { - fn from_sql(ty: &Type, raw: &'a [u8]) -> Result { - let Json(json_value) = Json::::from_sql(ty, raw)?; - let fields: Self = serde_json::from_value(json_value)?; - Ok(fields) - } - accepts!(JSON, JSONB); -} - -impl ToSql for ExtraFields { - fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result { - let value = serde_json::to_value(self)?; - Json(value).to_sql(ty, out) - } - - accepts!(JSON, JSONB); - to_sql_checked!(); -} - -impl<'a> FromSql<'a> for Actor { - fn from_sql(ty: &Type, raw: &'a [u8]) -> Result { - let Json(json_value) = Json::::from_sql(ty, raw)?; - let actor: Self = serde_json::from_value(json_value)?; - Ok(actor) - } - accepts!(JSON, JSONB); -} +json_from_sql!(Actor); #[derive(Clone, FromSql)] #[postgres(name = "actor_profile")]