use alloc::string::ToString;
use alloc::vec::Vec;
use super::decoder::{
BoolDecoder, DateVerbatimDecoder, DecimalTextDecoder, Decoder, Int64OverflowToTextDecoder,
IntDecoder, IntervalVerbatimDecoder, JsonCanonicalDecoder, JsonVerbatimDecoder,
MySqlBinaryDecoder, NullDecoder, PgByteaBinaryDecoder, PgByteaTextModeDecoder, RealDecoder,
TextDecoder, TimeVerbatimDecoder, TimestampTzVerbatimDecoder, TimestampVerbatimDecoder,
UuidBlob16Decoder, UuidText36Decoder,
};
use super::error::DecodeError;
use super::type_map::{TypeMap, TypeMapDefaults};
use crate::encoding::Value;
use crate::wal2json::{Wal2Json, Wal2JsonColumn};
impl<S, B> Decoder<Wal2Json, S, B> for NullDecoder {
fn decode(&self, _payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
Ok(Value::Null)
}
}
impl<S, B> Decoder<Wal2Json, S, B> for BoolDecoder {
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Bool(b) => Ok(Value::Integer(i64::from(*b))),
serde_json::Value::Number(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON boolean",
actual: "JSON number",
}),
serde_json::Value::String(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON boolean",
actual: "JSON string",
}),
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON boolean",
actual: "JSON array or object",
})
}
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for IntDecoder {
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Number(n) => match n.as_i64() {
Some(i) => Ok(Value::Integer(i)),
None => Err(DecodeError::IntegerOverflow {
column: payload.column_name.to_string(),
digits: n.to_string(),
}),
},
serde_json::Value::Bool(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON integer number",
actual: "JSON boolean",
}),
serde_json::Value::String(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON integer number",
actual: "JSON string",
}),
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON integer number",
actual: "JSON array or object",
})
}
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for Int64OverflowToTextDecoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Number(n) => match n.as_i64() {
Some(i) => Ok(Value::Integer(i)),
None => Ok(Value::Text(S::from(n.to_string()))),
},
serde_json::Value::String(s)
if s.trim_start_matches('-')
.chars()
.all(|c| c.is_ascii_digit()) =>
{
match s.parse::<i64>() {
Ok(i) => Ok(Value::Integer(i)),
Err(_) => Ok(Value::Text(S::from(s.clone()))),
}
}
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON integer number or numeric string",
actual: "other JSON shape",
}),
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for RealDecoder {
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Number(n) => match n.as_f64() {
Some(f) => Ok(normalize_real(f)),
None => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "IEEE 754 float number",
actual: "arbitrary-precision JSON number",
}),
},
serde_json::Value::Bool(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "IEEE 754 float number",
actual: "JSON boolean",
}),
serde_json::Value::String(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "IEEE 754 float number",
actual: "JSON string",
}),
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "IEEE 754 float number",
actual: "JSON array or object",
})
}
}
}
}
#[inline]
fn normalize_real<S, B>(f: f64) -> Value<S, B> {
if f.is_nan() {
Value::Null
} else if f == 0.0 {
Value::Real(0.0)
} else {
Value::Real(f)
}
}
impl<S, B> Decoder<Wal2Json, S, B> for TextDecoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => Ok(Value::Text(S::from(s.clone()))),
serde_json::Value::Bool(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string",
actual: "JSON boolean",
}),
serde_json::Value::Number(_) => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string",
actual: "JSON number",
}),
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string",
actual: "JSON array or object",
})
}
}
}
}
macro_rules! not_yet_impl {
($decoder:ty) => {
impl<S, B> Decoder<Wal2Json, S, B> for $decoder {
fn decode(&self, _payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
Err(DecodeError::NotYetImplemented {
decoder: stringify!($decoder),
})
}
}
};
}
impl<S, B> Decoder<Wal2Json, S, B> for PgByteaTextModeDecoder
where
B: From<Vec<u8>>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => match super::bytes_helpers::decode_pg_hex_escape(s) {
Ok(bytes) => Ok(Value::Blob(B::from(bytes))),
Err(at) => Err(DecodeError::InvalidHexEscape {
column: payload.column_name.to_string(),
at,
}),
},
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string with \\xHEX prefix",
actual: "other JSON shape",
}),
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for UuidBlob16Decoder
where
B: From<Vec<u8>>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => match super::uuid_helpers::parse_uuid(s) {
Ok(bytes) => Ok(Value::Blob(B::from(bytes.to_vec()))),
Err(source_len) => Err(DecodeError::InvalidUuid {
column: payload.column_name.to_string(),
source_len,
}),
},
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON UUID string",
actual: "other JSON shape",
}),
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for UuidText36Decoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => {
match super::uuid_helpers::preserve_or_canonicalize_uuid_text(s) {
Ok(canonical) => Ok(Value::Text(S::from(canonical))),
Err(source_len) => Err(DecodeError::InvalidUuid {
column: payload.column_name.to_string(),
source_len,
}),
}
}
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON UUID string",
actual: "other JSON shape",
}),
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for DecimalTextDecoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => Ok(Value::Text(S::from(s.clone()))),
serde_json::Value::Number(n) => Ok(Value::Text(S::from(n.to_string()))),
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string or number decimal",
actual: "other JSON shape",
}),
}
}
}
fn decode_wal2json_string_verbatim<S, B>(
payload: Wal2JsonColumn<'_>,
) -> Result<Value<S, B>, DecodeError>
where
S: From<alloc::string::String>,
{
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => Ok(Value::Text(S::from(s.clone()))),
_ => Err(DecodeError::WrongPayloadKind {
column: payload.column_name.to_string(),
expected: "JSON string",
actual: "other JSON shape",
}),
}
}
macro_rules! verbatim_impl {
($decoder:ty) => {
impl<S, B> Decoder<Wal2Json, S, B> for $decoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
decode_wal2json_string_verbatim(payload)
}
}
};
}
verbatim_impl!(TimestampVerbatimDecoder);
verbatim_impl!(TimestampTzVerbatimDecoder);
verbatim_impl!(DateVerbatimDecoder);
verbatim_impl!(TimeVerbatimDecoder);
verbatim_impl!(IntervalVerbatimDecoder);
impl<S, B> Decoder<Wal2Json, S, B> for JsonVerbatimDecoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => Ok(Value::Text(S::from(s.clone()))),
other => match crate::wire::json_helpers::serialize_verbatim(other) {
Ok(text) => Ok(Value::Text(S::from(text))),
Err(error) => Err(DecodeError::JsonNotSerializable {
column: payload.column_name.to_string(),
error,
}),
},
}
}
}
impl<S, B> Decoder<Wal2Json, S, B> for JsonCanonicalDecoder
where
S: From<alloc::string::String>,
{
fn decode(&self, payload: Wal2JsonColumn<'_>) -> Result<Value<S, B>, DecodeError> {
match payload.value {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::String(s) => {
let canon = crate::wire::json_helpers::canonicalize_string(s);
Ok(Value::Text(S::from(canon)))
}
other => match crate::wire::json_helpers::canonicalize_to_string(other) {
Ok(text) => Ok(Value::Text(S::from(text))),
Err(error) => Err(DecodeError::JsonNotSerializable {
column: payload.column_name.to_string(),
error,
}),
},
}
}
}
not_yet_impl!(PgByteaBinaryDecoder);
not_yet_impl!(MySqlBinaryDecoder);
impl<S, B> TypeMapDefaults<S, B> for Wal2Json
where
S: From<alloc::string::String>,
B: From<Vec<u8>>,
{
fn defaults() -> TypeMap<Self, S, B> {
TypeMap::new()
.with(alloc::sync::Arc::from("boolean"), BoolDecoder)
.with(alloc::sync::Arc::from("smallint"), IntDecoder)
.with(alloc::sync::Arc::from("integer"), IntDecoder)
.with(alloc::sync::Arc::from("bigint"), IntDecoder)
.with(alloc::sync::Arc::from("real"), RealDecoder)
.with(alloc::sync::Arc::from("double precision"), RealDecoder)
.with(alloc::sync::Arc::from("float4"), RealDecoder)
.with(alloc::sync::Arc::from("text"), TextDecoder)
.with(alloc::sync::Arc::from("varchar"), TextDecoder)
.with(alloc::sync::Arc::from("character varying"), TextDecoder)
.with(alloc::sync::Arc::from("character"), TextDecoder)
.with(alloc::sync::Arc::from("char"), TextDecoder)
.with(alloc::sync::Arc::from("name"), TextDecoder)
.with(alloc::sync::Arc::from("bytea"), PgByteaTextModeDecoder)
.with(alloc::sync::Arc::from("numeric"), DecimalTextDecoder)
.with(alloc::sync::Arc::from("decimal"), DecimalTextDecoder)
.with(
alloc::sync::Arc::from("timestamp"),
TimestampVerbatimDecoder,
)
.with(
alloc::sync::Arc::from("timestamp without time zone"),
TimestampVerbatimDecoder,
)
.with(
alloc::sync::Arc::from("timestamp with time zone"),
TimestampTzVerbatimDecoder,
)
.with(alloc::sync::Arc::from("date"), DateVerbatimDecoder)
.with(alloc::sync::Arc::from("time"), TimeVerbatimDecoder)
.with(
alloc::sync::Arc::from("time without time zone"),
TimeVerbatimDecoder,
)
.with(
alloc::sync::Arc::from("time with time zone"),
TimeVerbatimDecoder,
)
.with(alloc::sync::Arc::from("interval"), IntervalVerbatimDecoder)
.with(alloc::sync::Arc::from("float8"), RealDecoder)
.with(alloc::sync::Arc::from("json"), JsonVerbatimDecoder)
.with(alloc::sync::Arc::from("jsonb"), JsonVerbatimDecoder)
}
}