use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;
use serde::Deserialize;
use serde::de::{self, DeserializeOwned, IgnoredAny, MapAccess, SeqAccess, Visitor};
use crate::error::DrizzleError;
use crate::prelude::*;
use crate::relation::RelationDef;
use super::builder::BuildRow;
use super::row::QueryRow;
use super::store::RelEntry;
pub trait JsonObjectDecoder<'de>: Sized {
type State;
fn begin() -> Self::State;
fn decode_field<A>(state: &mut Self::State, key: &str, map: &mut A) -> Result<bool, A::Error>
where
A: MapAccess<'de>;
fn finish<E>(state: Self::State) -> Result<Self, E>
where
E: de::Error;
}
pub trait FromJsonObject: Sized {
fn from_json_str(json: &str, context: &str) -> Result<Self, DrizzleError>;
}
impl<T> FromJsonObject for T
where
T: for<'de> Deserialize<'de>,
{
#[inline]
fn from_json_str(json: &str, context: &str) -> Result<Self, DrizzleError> {
serde_json::from_str(json)
.map_err(|e| DrizzleError::Other(format!("failed to parse {context} JSON: {e}").into()))
}
}
pub trait DeserializeStore: Sized {
fn from_json_columns<F>(next: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut() -> Result<Option<String>, DrizzleError>;
fn from_named_json_columns<F>(lookup: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut(&str) -> Result<Option<String>, DrizzleError>;
}
impl DeserializeStore for () {
#[inline]
fn from_json_columns<F>(_next: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut() -> Result<Option<String>, DrizzleError>,
{
Ok(())
}
#[inline]
fn from_named_json_columns<F>(_lookup: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut(&str) -> Result<Option<String>, DrizzleError>,
{
Ok(())
}
}
impl<'de> JsonObjectDecoder<'de> for () {
type State = ();
#[inline]
fn begin() -> Self::State {}
#[inline]
fn decode_field<A>(_state: &mut Self::State, _key: &str, _map: &mut A) -> Result<bool, A::Error>
where
A: MapAccess<'de>,
{
Ok(false)
}
#[inline]
fn finish<E>(_state: Self::State) -> Result<Self, E>
where
E: de::Error,
{
Ok(())
}
}
impl<Rel, Data, Rest> DeserializeStore for RelEntry<Rel, Data, Rest>
where
Rel: RelationDef,
Data: FromJsonColumn,
Rest: DeserializeStore,
{
fn from_json_columns<F>(next: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut() -> Result<Option<String>, DrizzleError>,
{
let json = next()?;
let data = Data::from_json_column(json.as_deref(), Rel::NAME)
.map_err(|e| DrizzleError::Other(format!("relation '{}': {e}", Rel::NAME).into()))?;
let rest = Rest::from_json_columns(next)?;
Ok(Self::new(data, rest))
}
fn from_named_json_columns<F>(lookup: &mut F) -> Result<Self, DrizzleError>
where
F: FnMut(&str) -> Result<Option<String>, DrizzleError>,
{
let json = lookup(Rel::NAME)?;
let data = Data::from_json_column(json.as_deref(), Rel::NAME)
.map_err(|e| DrizzleError::Other(format!("relation '{}': {e}", Rel::NAME).into()))?;
let rest = Rest::from_named_json_columns(lookup)?;
Ok(Self::new(data, rest))
}
}
impl<'de, Rel, Data, Rest> JsonObjectDecoder<'de> for RelEntry<Rel, Data, Rest>
where
Rel: RelationDef,
Data: FromJsonField<'de>,
Rest: JsonObjectDecoder<'de>,
{
type State = (Option<Data>, Rest::State);
fn begin() -> Self::State {
(None, Rest::begin())
}
fn decode_field<A>(state: &mut Self::State, key: &str, map: &mut A) -> Result<bool, A::Error>
where
A: MapAccess<'de>,
{
if key == Rel::NAME {
state.0 = Some(Data::decode_json_field(map, Rel::NAME)?);
return Ok(true);
}
Rest::decode_field(&mut state.1, key, map)
}
fn finish<E>(state: Self::State) -> Result<Self, E>
where
E: de::Error,
{
let data = match state.0 {
Some(data) => data,
None => Data::missing_json_field(Rel::NAME)?,
};
let rest = Rest::finish(state.1)?;
Ok(Self::new(data, rest))
}
}
pub trait FromJsonColumn: Sized {
fn from_json_column(json: Option<&str>, context: &str) -> Result<Self, DrizzleError>;
}
pub trait FromJsonField<'de>: Sized {
fn decode_json_field<A>(map: &mut A, context: &str) -> Result<Self, A::Error>
where
A: MapAccess<'de>;
fn missing_json_field<E>(context: &str) -> Result<Self, E>
where
E: de::Error;
}
impl<T> FromJsonColumn for Vec<T>
where
T: for<'de> Deserialize<'de>,
{
fn from_json_column(json: Option<&str>, context: &str) -> Result<Self, DrizzleError> {
match json {
Some(json) => serde_json::from_str::<JsonVec<T>>(json)
.map(|items| items.0)
.map_err(|e| {
DrizzleError::Other(format!("failed to parse {context} JSON: {e}").into())
}),
None => Ok(Self::new()),
}
}
}
impl<'de, T> FromJsonField<'de> for Vec<T>
where
T: Deserialize<'de>,
{
fn decode_json_field<A>(map: &mut A, _context: &str) -> Result<Self, A::Error>
where
A: MapAccess<'de>,
{
map.next_value::<JsonVec<T>>().map(|items| items.0)
}
fn missing_json_field<E>(_context: &str) -> Result<Self, E>
where
E: de::Error,
{
Ok(Self::new())
}
}
impl<T> FromJsonColumn for Option<T>
where
T: for<'de> Deserialize<'de>,
{
fn from_json_column(json: Option<&str>, context: &str) -> Result<Self, DrizzleError> {
match json {
Some(json) => serde_json::from_str(json).map_err(|e| {
DrizzleError::Other(format!("failed to parse {context} JSON: {e}").into())
}),
None => Ok(None),
}
}
}
impl<'de, T> FromJsonField<'de> for Option<T>
where
T: Deserialize<'de>,
{
fn decode_json_field<A>(map: &mut A, _context: &str) -> Result<Self, A::Error>
where
A: MapAccess<'de>,
{
map.next_value()
}
fn missing_json_field<E>(_context: &str) -> Result<Self, E>
where
E: de::Error,
{
Ok(None)
}
}
impl<Base, Store> FromJsonColumn for QueryRow<Base, Store>
where
Self: for<'de> Deserialize<'de>,
{
fn from_json_column(json: Option<&str>, context: &str) -> Result<Self, DrizzleError> {
let Some(json) = json else {
return Err(DrizzleError::Other(
format!("missing JSON column for {context}").into(),
));
};
let row: Option<Self> = serde_json::from_str(json).map_err(|e| {
DrizzleError::Other(format!("failed to parse {context} JSON: {e}").into())
})?;
row.ok_or_else(|| {
DrizzleError::Other(format!("expected non-null relation '{context}'").into())
})
}
}
impl<'de, Base, Store> FromJsonField<'de> for QueryRow<Base, Store>
where
Self: Deserialize<'de>,
{
fn decode_json_field<A>(map: &mut A, context: &str) -> Result<Self, A::Error>
where
A: MapAccess<'de>,
{
let row: Option<Self> = map.next_value()?;
row.ok_or_else(|| de::Error::custom(format!("expected non-null relation '{context}'")))
}
fn missing_json_field<E>(context: &str) -> Result<Self, E>
where
E: de::Error,
{
Err(de::Error::custom(format!(
"missing non-null relation '{context}'"
)))
}
}
impl<'de, Base, Store> Deserialize<'de> for QueryRow<Base, Store>
where
Base: JsonObjectDecoder<'de>,
Store: JsonObjectDecoder<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct QueryRowVisitor<Base, Store>(PhantomData<(Base, Store)>);
impl<'de, Base, Store> Visitor<'de> for QueryRowVisitor<Base, Store>
where
Base: JsonObjectDecoder<'de>,
Store: JsonObjectDecoder<'de>,
{
type Value = QueryRow<Base, Store>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a relation row JSON object")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut base = Base::begin();
let mut store = Store::begin();
while let Some(key) = map.next_key::<Cow<'de, str>>()? {
let key = key.as_ref();
if Base::decode_field(&mut base, key, &mut map)? {
continue;
}
if Store::decode_field(&mut store, key, &mut map)? {
continue;
}
map.next_value::<IgnoredAny>()?;
}
Ok(QueryRow::new(Base::finish(base)?, Store::finish(store)?))
}
}
deserializer.deserialize_map(QueryRowVisitor::<Base, Store>(PhantomData))
}
}
#[derive(Debug)]
pub struct JsonQueryRow {
base: String,
rels: Vec<(String, Option<String>)>,
}
impl JsonQueryRow {
pub fn into_row<Base, Rels>(mut self) -> Result<Rels::Row, DrizzleError>
where
Base: FromJsonObject,
Rels: BuildRow<Base>,
Rels::Store: DeserializeStore,
{
let base = Base::from_json_str(&self.base, "base")?;
let store = Rels::Store::from_named_json_columns(&mut |name| Ok(self.take_rel(name)))?;
Ok(Rels::assemble(base, store))
}
fn take_rel(&mut self, name: &str) -> Option<String> {
self.rels
.iter_mut()
.find(|(rel, _)| rel == name)
.and_then(|(_, json)| json.take())
}
}
impl<'de> Deserialize<'de> for JsonQueryRow {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct RowVisitor;
impl<'de> Visitor<'de> for RowVisitor {
type Value = JsonQueryRow;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a relational query row object")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut base = None;
let mut rels = Vec::new();
while let Some(key) = map.next_key::<Cow<'de, str>>()? {
if key == "__base" {
base = Some(map.next_value()?);
} else if let Some(name) = key.strip_prefix("__rel_") {
rels.push((name.to_owned(), map.next_value()?));
} else {
map.next_value::<IgnoredAny>()?;
}
}
let base = base.ok_or_else(|| de::Error::missing_field("__base"))?;
Ok(JsonQueryRow { base, rels })
}
}
deserializer.deserialize_map(RowVisitor)
}
}
pub type RawJson = serde_json::Value;
#[doc(hidden)]
pub fn decode_json_text<T, E>(raw: RawJson, column: &str) -> Result<T, E>
where
T: DeserializeOwned,
E: de::Error,
{
let decoded = match raw {
serde_json::Value::String(text) => serde_json::from_str(&text),
value => serde_json::from_value(value),
};
decoded.map_err(|e| E::custom(format!("field '{column}': invalid JSON: {e}")))
}
#[doc(hidden)]
pub fn decode_json_bytes<T, E>(bytes: &[u8], column: &str) -> Result<T, E>
where
T: DeserializeOwned,
E: de::Error,
{
serde_json::from_slice(bytes)
.map_err(|e| E::custom(format!("field '{column}': invalid JSON blob: {e}")))
}
#[doc(hidden)]
pub fn decode_enum_value<T, E>(raw: RawJson, column: &str) -> Result<T, E>
where
T: TryFrom<i64> + FromStr,
<T as FromStr>::Err: fmt::Display,
E: de::Error,
{
match raw {
serde_json::Value::Number(number) => {
let value = number.as_i64().ok_or_else(|| {
E::custom(format!(
"enum field '{column}': invalid integer value {number}"
))
})?;
T::try_from(value).map_err(|_| {
E::custom(format!(
"enum field '{column}': invalid integer value {value}"
))
})
}
serde_json::Value::String(value) => T::from_str(&value)
.map_err(|error| E::custom(format!("enum field '{column}': {error}"))),
value => Err(E::custom(format!(
"enum field '{column}': expected string or integer, got {value}"
))),
}
}
pub struct JsonBool(pub bool);
impl<'de> Deserialize<'de> for JsonBool {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct BoolVisitor;
impl Visitor<'_> for BoolVisitor {
type Value = JsonBool;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a boolean or integer")
}
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonBool(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonBool(value != 0))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonBool(value != 0))
}
}
deserializer.deserialize_any(BoolVisitor)
}
}
pub struct JsonOptionalBool(pub Option<bool>);
impl<'de> Deserialize<'de> for JsonOptionalBool {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct OptionalBoolVisitor;
impl<'de> Visitor<'de> for OptionalBoolVisitor {
type Value = JsonOptionalBool;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a nullable boolean or integer")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonOptionalBool(None))
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonOptionalBool(None))
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
JsonBool::deserialize(deserializer).map(|value| JsonOptionalBool(Some(value.0)))
}
}
deserializer.deserialize_option(OptionalBoolVisitor)
}
}
struct JsonVec<T>(Vec<T>);
impl<'de, T> Deserialize<'de> for JsonVec<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct JsonVecVisitor<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for JsonVecVisitor<T>
where
T: Deserialize<'de>,
{
type Value = JsonVec<T>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a JSON array or null")
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonVec(Vec::new()))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(JsonVec(Vec::new()))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = seq.next_element::<Option<T>>()? {
if let Some(value) = value {
out.push(value);
}
}
Ok(JsonVec(out))
}
}
deserializer.deserialize_any(JsonVecVisitor::<T>(PhantomData))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_row(json: &str) -> JsonQueryRow {
serde_json::from_str(json).expect("row should deserialize")
}
#[test]
fn json_query_row_splits_base_and_relation_columns() {
let mut row = parse_row(
r#"{"__base":"{\"id\":1}","__rel_posts":"[]","__rel_author":null,"noise":42}"#,
);
assert_eq!(row.base, r#"{"id":1}"#);
assert_eq!(row.take_rel("posts").as_deref(), Some("[]"));
assert_eq!(row.take_rel("posts"), None, "each relation decodes once");
assert_eq!(row.take_rel("author"), None, "SQL NULL maps to None");
assert_eq!(row.take_rel("missing"), None);
}
#[test]
fn json_query_row_requires_base_column() {
let error = serde_json::from_str::<JsonQueryRow>(r#"{"__rel_posts":"[]"}"#)
.expect_err("a row without __base is malformed");
assert!(error.to_string().contains("__base"));
}
#[test]
fn into_row_parses_base_json() {
let row = parse_row(r#"{"__base":"{\"id\":7,\"name\":\"a\"}"}"#);
let value = row
.into_row::<serde_json::Value, ()>()
.expect("base JSON should parse");
assert_eq!(value["id"], 7);
assert_eq!(value["name"], "a");
}
#[test]
fn json_text_fields_parse_the_embedded_document() {
let embedded = RawJson::String(r#"{"id":7}"#.into());
let value: serde_json::Value =
decode_json_text::<_, serde_json::Error>(embedded, "meta").unwrap();
assert_eq!(value["id"], 7);
let native: Vec<i64> =
decode_json_text::<_, serde_json::Error>(serde_json::json!([1, 2]), "tags").unwrap();
assert_eq!(native, [1, 2]);
let error =
decode_json_text::<Vec<i64>, serde_json::Error>(RawJson::String("oops".into()), "tags")
.unwrap_err();
assert!(error.to_string().contains("field 'tags'"));
}
#[test]
fn json_bytes_fields_report_the_column() {
let tags: Vec<i64> = decode_json_bytes::<_, serde_json::Error>(b"[3]", "tags").unwrap();
assert_eq!(tags, [3]);
let error = decode_json_bytes::<Vec<i64>, serde_json::Error>(b"{", "tags").unwrap_err();
assert!(error.to_string().contains("field 'tags'"));
}
#[test]
fn enum_values_accept_names_and_integers() {
let from_integer: i32 =
decode_enum_value::<_, serde_json::Error>(serde_json::json!(5), "rank").unwrap();
assert_eq!(from_integer, 5);
let from_name: i32 =
decode_enum_value::<_, serde_json::Error>(serde_json::json!("6"), "rank").unwrap();
assert_eq!(from_name, 6);
let error = decode_enum_value::<i32, serde_json::Error>(serde_json::json!(true), "rank")
.unwrap_err();
assert!(error.to_string().contains("expected string or integer"));
}
#[test]
fn into_row_reports_invalid_base_json() {
let row = parse_row(r#"{"__base":"not json"}"#);
let error = row
.into_row::<serde_json::Value, ()>()
.expect_err("invalid base JSON should fail");
assert!(error.to_string().contains("base"));
}
}