use alibi_core::AuthResult;
#[derive(Clone, Debug, serde::Serialize)]
#[serde(transparent)]
pub struct JsonMetadata(serde_json::Value, #[serde(skip)] Option<String>);
impl JsonMetadata {
pub(crate) fn for_backend(
value: serde_json::Value,
backend: sea_orm::DbBackend,
) -> Result<Self, serde_json::Error> {
let text = if backend == sea_orm::DbBackend::Sqlite {
Some(alibi_core::utils::json::to_string(&value)?)
} else {
None
};
Ok(Self(value, text))
}
}
impl PartialEq for JsonMetadata {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'de> serde::Deserialize<'de> for JsonMetadata {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
alibi_core::utils::json::deserialize_value(deserializer).map(Self::from)
}
}
impl From<serde_json::Value> for JsonMetadata {
fn from(value: serde_json::Value) -> Self {
Self(value, None)
}
}
impl From<JsonMetadata> for serde_json::Value {
fn from(value: JsonMetadata) -> Self {
value.0
}
}
impl sea_orm::TryGetable for JsonMetadata {
fn try_get_by<I: sea_orm::ColIdx>(
result: &sea_orm::QueryResult,
index: I,
) -> Result<Self, sea_orm::TryGetError> {
if let Ok(value) = serde_json::Value::try_get_by(result, index) {
Ok(Self::from(value))
} else {
let text = String::try_get_by(result, index)?;
alibi_core::utils::json::from_slice(text.as_bytes()).map_err(|error| {
sea_orm::TryGetError::DbErr(sea_orm::DbErr::Custom(error.to_string()))
})
}
}
}
impl From<JsonMetadata> for sea_orm::Value {
fn from(value: JsonMetadata) -> Self {
match value.1 {
Some(text) => Self::String(Some(text)),
None => Self::Json(Some(Box::new(value.0))),
}
}
}
impl sea_orm::sea_query::ValueType for JsonMetadata {
fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> {
match v {
sea_orm::Value::Json(Some(v)) => Ok(Self::from(*v)),
sea_orm::Value::String(Some(text)) => {
alibi_core::utils::json::from_slice::<serde_json::Value>(text.as_bytes())
.map(Self::from)
.map_err(|_error| sea_orm::sea_query::ValueTypeErr)
}
_ => Err(sea_orm::sea_query::ValueTypeErr),
}
}
fn type_name() -> String {
"JsonMetadata".to_owned()
}
fn array_type() -> sea_orm::sea_query::ArrayType {
sea_orm::sea_query::ArrayType::Json
}
fn column_type() -> sea_orm::sea_query::ColumnType {
sea_orm::sea_query::ColumnType::Json
}
}
impl sea_orm::sea_query::Nullable for JsonMetadata {
fn null() -> sea_orm::Value {
sea_orm::Value::Json(None)
}
}
impl sea_orm::IntoActiveValue<Self> for JsonMetadata {
fn into_active_value(self) -> sea_orm::ActiveValue<Self> {
sea_orm::ActiveValue::set(self)
}
}
impl std::ops::Deref for JsonMetadata {
type Target = serde_json::Value;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait MetadataBinding: Sized {
fn prepare(self, backend: sea_orm::DbBackend) -> AuthResult<Self>;
}
impl MetadataBinding for serde_json::Value {
fn prepare(self, _backend: sea_orm::DbBackend) -> AuthResult<Self> {
Ok(alibi_core::utils::json::to_value(&self)?)
}
}
impl MetadataBinding for JsonMetadata {
fn prepare(self, backend: sea_orm::DbBackend) -> AuthResult<Self> {
let value = alibi_core::utils::json::to_value(&self.0)?;
Ok(Self::for_backend(value, backend)?)
}
}
impl<T: MetadataBinding> MetadataBinding for Option<T> {
fn prepare(self, backend: sea_orm::DbBackend) -> AuthResult<Self> {
self.map(|value| value.prepare(backend)).transpose()
}
}