From 79e46e9c09fdb397ed131613f7f840c2e364ee03 Mon Sep 17 00:00:00 2001 From: Per Stark Date: Mon, 25 May 2026 13:38:28 +0200 Subject: [PATCH] refactor: extract serde helpers from stored_object! macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move FlexibleIdVisitor, deserialize_flexible_id, and four datetime serde helpers from repeating inside every macro expansion into a shared common/src/storage/types/serde_helpers.rs module. 14 macro invocations × 6 items = ~84 fewer redundant function definitions. Fragile cross-module imports (file_info::deserialize_flexible_id etc.) are updated to point to the canonical module. --- common/src/storage/types/analytics.rs | 2 +- .../storage/types/knowledge_relationship.rs | 2 +- common/src/storage/types/message.rs | 2 + common/src/storage/types/mod.rs | 95 ++----------------- common/src/storage/types/serde_helpers.rs | 80 ++++++++++++++++ common/src/storage/types/system_settings.rs | 2 +- html-router/src/routes/search/handlers.rs | 3 +- 7 files changed, 94 insertions(+), 92 deletions(-) create mode 100644 common/src/storage/types/serde_helpers.rs diff --git a/common/src/storage/types/analytics.rs b/common/src/storage/types/analytics.rs index a841a2a..79a477d 100644 --- a/common/src/storage/types/analytics.rs +++ b/common/src/storage/types/analytics.rs @@ -1,4 +1,4 @@ -use crate::storage::types::{file_info::deserialize_flexible_id, user::User, StoredObject}; +use crate::storage::types::{serde_helpers::deserialize_flexible_id, user::User, StoredObject}; use serde::{Deserialize, Serialize}; use crate::{error::AppError, storage::db::SurrealDbClient}; diff --git a/common/src/storage/types/knowledge_relationship.rs b/common/src/storage/types/knowledge_relationship.rs index 82134cd..f63c8c7 100644 --- a/common/src/storage/types/knowledge_relationship.rs +++ b/common/src/storage/types/knowledge_relationship.rs @@ -1,4 +1,4 @@ -use crate::storage::types::file_info::deserialize_flexible_id; +use crate::storage::types::serde_helpers::deserialize_flexible_id; use crate::{error::AppError, storage::db::SurrealDbClient}; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/common/src/storage/types/message.rs b/common/src/storage/types/message.rs index 4070f79..a120816 100644 --- a/common/src/storage/types/message.rs +++ b/common/src/storage/types/message.rs @@ -1,6 +1,8 @@ #![allow(clippy::module_name_repetitions)] use uuid::Uuid; +use std::fmt; + use crate::stored_object; #[derive(Deserialize, Debug, Clone, Serialize, PartialEq)] diff --git a/common/src/storage/types/mod.rs b/common/src/storage/types/mod.rs index eae257f..4609212 100644 --- a/common/src/storage/types/mod.rs +++ b/common/src/storage/types/mod.rs @@ -1,5 +1,6 @@ #![allow(clippy::unsafe_derive_deserialize)] use serde::{Deserialize, Serialize}; +pub mod serde_helpers; pub mod analytics; pub mod conversation; pub mod file_info; @@ -25,97 +26,15 @@ pub trait StoredObject: Serialize + for<'de> Deserialize<'de> { #[macro_export] macro_rules! stored_object { ($(#[$struct_attr:meta])* $name:ident, $table:expr, {$($(#[$field_attr:meta])* $field:ident: $ty:ty),*}) => { - use serde::{Deserialize, Deserializer, Serialize}; - use surrealdb::sql::Thing; + use serde::{Deserialize, Serialize}; use $crate::storage::types::StoredObject; - use serde::de::{self, Visitor}; - use std::fmt; + #[allow(unused_imports)] + use $crate::storage::types::serde_helpers::{ + deserialize_flexible_id, serialize_datetime, deserialize_datetime, + serialize_option_datetime, deserialize_option_datetime, + }; use chrono::{DateTime, Utc }; - struct FlexibleIdVisitor; - - impl<'de> Visitor<'de> for FlexibleIdVisitor { - type Value = String; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string or a Thing") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - Ok(value.to_string()) - } - - fn visit_string(self, value: String) -> Result - where - E: de::Error, - { - Ok(value) - } - - fn visit_map(self, map: A) -> Result - where - A: de::MapAccess<'de>, - { - // Try to deserialize as Thing - let thing = Thing::deserialize(de::value::MapAccessDeserializer::new(map))?; - Ok(thing.id.to_raw()) - } - } - - pub fn deserialize_flexible_id<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(FlexibleIdVisitor) - } - - fn serialize_datetime(date: &DateTime, serializer: S) -> Result - where - S: serde::Serializer, - { - Into::::into(*date).serialize(serializer) - } - - fn deserialize_datetime<'de, D>(deserializer: D) -> Result, D::Error> - where - D: serde::Deserializer<'de>, - { - let dt = surrealdb::sql::Datetime::deserialize(deserializer)?; - Ok(DateTime::::from(dt)) - } - - #[allow(dead_code)] - #[allow(clippy::ref_option)] - fn serialize_option_datetime( - date: &Option>, - serializer: S, - ) -> Result - where - S: serde::Serializer, - { - match date { - Some(dt) => serializer - .serialize_some(&Into::::into(*dt)), - None => serializer.serialize_none(), - } - } - - #[allow(dead_code)] - #[allow(clippy::ref_option)] - fn deserialize_option_datetime<'de, D>( - deserializer: D, - ) -> Result>, D::Error> - where - D: serde::Deserializer<'de>, - { - let value = Option::::deserialize(deserializer)?; - Ok(value.map(DateTime::::from)) - } - - $(#[$struct_attr])* #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct $name { diff --git a/common/src/storage/types/serde_helpers.rs b/common/src/storage/types/serde_helpers.rs new file mode 100644 index 0000000..2ae7553 --- /dev/null +++ b/common/src/storage/types/serde_helpers.rs @@ -0,0 +1,80 @@ +use chrono::{DateTime, Utc}; +use serde::de::{self, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use surrealdb::sql::Thing; + +struct FlexibleIdVisitor; + +impl<'de> Visitor<'de> for FlexibleIdVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string or a Thing") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(value.to_string()) + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + Ok(value) + } + + fn visit_map(self, map: A) -> Result + where + A: de::MapAccess<'de>, + { + let thing = Thing::deserialize(de::value::MapAccessDeserializer::new(map))?; + Ok(thing.id.to_raw()) + } +} + +pub fn deserialize_flexible_id<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + deserializer.deserialize_any(FlexibleIdVisitor) +} + +pub fn serialize_datetime(date: &DateTime, serializer: S) -> Result +where + S: Serializer, +{ + Into::::into(*date).serialize(serializer) +} + +pub fn deserialize_datetime<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let dt = surrealdb::sql::Datetime::deserialize(deserializer)?; + Ok(DateTime::::from(dt)) +} + +pub fn serialize_option_datetime( + date: &Option>, + serializer: S, +) -> Result +where + S: Serializer, +{ + match date { + Some(dt) => serializer.serialize_some(&Into::::into(*dt)), + None => serializer.serialize_none(), + } +} + +pub fn deserialize_option_datetime<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + let value = Option::::deserialize(deserializer)?; + Ok(value.map(DateTime::::from)) +} diff --git a/common/src/storage/types/system_settings.rs b/common/src/storage/types/system_settings.rs index f387f43..f387c61 100644 --- a/common/src/storage/types/system_settings.rs +++ b/common/src/storage/types/system_settings.rs @@ -1,4 +1,4 @@ -use crate::storage::types::file_info::deserialize_flexible_id; +use crate::storage::types::serde_helpers::deserialize_flexible_id; use serde::{Deserialize, Serialize}; use crate::{error::AppError, storage::db::SurrealDbClient, storage::types::StoredObject}; diff --git a/html-router/src/routes/search/handlers.rs b/html-router/src/routes/search/handlers.rs index f2611e1..244590f 100644 --- a/html-router/src/routes/search/handlers.rs +++ b/html-router/src/routes/search/handlers.rs @@ -9,7 +9,8 @@ use axum::{ response::IntoResponse, }; use common::storage::types::{ - text_content::{deserialize_flexible_id, TextContent}, + serde_helpers::deserialize_flexible_id, + text_content::TextContent, StoredObject, }; use retrieval_pipeline::{RetrievalConfig, SearchResult, SearchTarget, StrategyOutput};