use sqlx::postgres::{PgHasArrayType, PgTypeInfo};
#[cfg(test)]
mod test;
#[doc(hidden)]
pub const JSON_OID: sqlx::postgres::types::Oid = sqlx::postgres::types::Oid(114);
#[doc(hidden)]
pub const JSONB_OID: sqlx::postgres::types::Oid = sqlx::postgres::types::Oid(3802);
#[macro_export]
macro_rules! sqlx_json_decode {
($type:ty) => {
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for $type {
fn decode(
value: sqlx::postgres::PgValueRef<'r>,
) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
let buf = $crate::decode_json(value)?;
serde_json::from_slice(buf).map_err(Into::into)
}
}
impl sqlx::Type<sqlx::Postgres> for $type {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_oid($crate::JSONB_OID)
}
fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
*ty == sqlx::postgres::PgTypeInfo::with_oid($crate::JSONB_OID)
|| *ty == sqlx::postgres::PgTypeInfo::with_oid($crate::JSON_OID)
}
}
};
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoxedRawValue(Box<serde_json::value::RawValue>);
#[cfg(feature = "schemars")]
impl schemars::JsonSchema for BoxedRawValue {
fn is_referenceable() -> bool {
false
}
fn schema_name() -> String {
serde_json::value::RawValue::schema_name()
}
fn schema_id() -> std::borrow::Cow<'static, str> {
serde_json::value::RawValue::schema_id()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
serde_json::value::RawValue::json_schema(gen)
}
}
impl std::ops::Deref for BoxedRawValue {
type Target = serde_json::value::RawValue;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<BoxedRawValue> for Box<serde_json::value::RawValue> {
fn from(raw_value: BoxedRawValue) -> Self {
raw_value.0
}
}
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for BoxedRawValue {
fn decode(
value: <sqlx::Postgres as sqlx::database::HasValueRef<'r>>::ValueRef,
) -> Result<Self, sqlx::error::BoxDynError> {
let buf = decode_json(value)?;
let string = std::str::from_utf8(buf)?;
let raw_value = serde_json::value::RawValue::from_string(string.to_owned())?;
Ok(BoxedRawValue(raw_value))
}
}
impl PgHasArrayType for BoxedRawValue {
fn array_type_info() -> PgTypeInfo {
serde_json::value::RawValue::array_type_info()
}
fn array_compatible(ty: &PgTypeInfo) -> bool {
serde_json::value::RawValue::array_compatible(ty)
}
}
impl<'r> sqlx::Encode<'r, sqlx::Postgres> for BoxedRawValue {
fn encode_by_ref(
&self,
out: &mut <sqlx::Postgres as sqlx::database::HasArguments<'r>>::ArgumentBuffer,
) -> sqlx::encode::IsNull {
let j = sqlx::types::Json(&self.0);
j.encode_by_ref(out)
}
}
impl sqlx::Type<sqlx::Postgres> for BoxedRawValue {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_oid(JSONB_OID)
}
fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
*ty == sqlx::postgres::PgTypeInfo::with_oid(JSONB_OID)
|| *ty == sqlx::postgres::PgTypeInfo::with_oid(JSON_OID)
}
}
pub fn decode_json(
value: <sqlx::Postgres as sqlx::database::HasValueRef<'_>>::ValueRef,
) -> Result<&'_ [u8], sqlx::error::BoxDynError> {
use sqlx::ValueRef;
let is_jsonb = value.type_info().as_ref() == &sqlx::postgres::PgTypeInfo::with_oid(JSONB_OID);
let mut buf = <&[u8] as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
if is_jsonb {
assert_eq!(
buf[0], 1,
"unsupported JSONB format version {}; please open an issue",
buf[0]
);
buf = &buf[1..];
}
Ok(buf)
}