use crate::{Serializer, SerializerError};
use reinhardt_db::orm::Model;
use serde_json::{Value, json};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
pub trait UrlReverser: Send + Sync {
fn reverse(&self, name: &str, params: &HashMap<String, String>) -> Result<String, String>;
}
pub struct HyperlinkedModelSerializer<M: Model> {
view_name: String,
url_field_name: String,
url_reverser: Option<Arc<dyn UrlReverser>>,
_phantom: PhantomData<M>,
}
impl<M: Model> HyperlinkedModelSerializer<M> {
pub fn new(view_name: impl Into<String>, url_reverser: Option<Arc<dyn UrlReverser>>) -> Self {
Self {
view_name: view_name.into(),
url_field_name: String::from("url"),
url_reverser,
_phantom: PhantomData,
}
}
pub fn url_field_name(mut self, name: impl Into<String>) -> Self {
self.url_field_name = name.into();
self
}
fn get_url(&self, instance: &M) -> Result<String, SerializerError>
where
M::PrimaryKey: serde::Serialize,
{
if let Some(pk) = instance.primary_key() {
let pk_str = serde_json::to_string(&pk)
.map_err(|e| SerializerError::Other {
message: format!("Primary key serialization error: {}", e),
})?
.trim_matches('"')
.to_string();
if let Some(reverser) = &self.url_reverser {
let mut params = HashMap::new();
params.insert(M::primary_key_field().to_string(), pk_str);
reverser
.reverse(&self.view_name, ¶ms)
.map_err(|e| SerializerError::Other {
message: format!("URL reversal error for view '{}': {}", self.view_name, e),
})
} else {
Ok(format!(
"/{}/{}/{}",
M::table_name(),
self.view_name,
pk_str
))
}
} else {
Err(SerializerError::Other {
message: String::from("Instance has no primary key"),
})
}
}
}
impl<M> Serializer for HyperlinkedModelSerializer<M>
where
M: Model,
M::PrimaryKey: serde::Serialize,
{
type Input = M;
type Output = String;
fn serialize(&self, input: &Self::Input) -> Result<Self::Output, SerializerError> {
let mut value: Value = serde_json::to_value(input).map_err(|e| SerializerError::Other {
message: format!("Serialization error: {}", e),
})?;
if let Value::Object(ref mut map) = value {
let url = self.get_url(input)?;
map.insert(self.url_field_name.clone(), json!(url));
}
serde_json::to_string(&value).map_err(|e| SerializerError::Other {
message: format!("Serialization error: {}", e),
})
}
fn deserialize(&self, output: &Self::Output) -> Result<Self::Input, SerializerError> {
serde_json::from_str(output).map_err(|e| SerializerError::Other {
message: format!("Deserialization error: {}", e),
})
}
}