#[cfg(feature = "url")]
use ::url::Url;
#[cfg(any(feature = "chrono", feature = "url"))]
use serde::Deserialize;
#[cfg(any(feature = "chrono", feature = "url"))]
use serde::Deserializer;
#[cfg(feature = "chrono")]
use serde::Serializer;
#[cfg(any(feature = "chrono", feature = "url"))]
use serde::de::Error as _;
#[cfg(feature = "big-decimal")]
use self::internal::BigDecimalPayload;
use self::internal::DurationPayload;
#[cfg(any(feature = "chrono", feature = "url"))]
use self::internal::ValidatedSequenceVisitor;
#[cfg(feature = "chrono")]
use crate::ValueWireEncodeError;
#[cfg(feature = "chrono")]
#[inline]
pub(crate) fn validate_instant_year(year: i32) -> Result<(), ValueWireEncodeError> {
if (0..=9999).contains(&year) {
Ok(())
} else {
Err(ValueWireEncodeError::InstantYearOutOfRange { year })
}
}
pub(crate) use crate::finite_float::float32;
pub(crate) use crate::finite_float::float32_vec;
pub(crate) use crate::finite_float::float64;
pub(crate) use crate::finite_float::float64_vec;
pub(crate) use crate::wide_integer::int128;
pub(crate) use crate::wide_integer::int128_vec;
pub(crate) use crate::wide_integer::uint128;
pub(crate) use crate::wide_integer::uint128_vec;
pub(crate) mod string_map;
pub(crate) mod string_map_vec;
#[cfg(feature = "json")]
pub(crate) mod json;
#[cfg(feature = "json")]
pub(crate) mod json_vec;
#[cfg(feature = "big-integer")]
mod decimal;
mod internal;
#[cfg(feature = "big-decimal")]
pub(crate) const MAX_BIG_DECIMAL_ABSOLUTE_SCALE: i64 = 150_000;
#[must_use]
#[cfg(feature = "big-decimal")]
#[inline(always)]
pub(crate) const fn is_valid_big_decimal_scale(scale: i64) -> bool {
scale.unsigned_abs() <= MAX_BIG_DECIMAL_ABSOLUTE_SCALE as u64
}
#[cfg(feature = "chrono")]
fn serialize_canonical<S, T, F>(value: &T, serializer: S, format: F) -> Result<S::Ok, S::Error>
where
S: Serializer,
F: FnOnce(&T) -> String,
{
serializer.serialize_str(&format(value))
}
#[cfg(any(feature = "chrono", feature = "url"))]
fn deserialize_canonical<'de, D, T, P, F>(
deserializer: D,
parse: P,
format: F,
) -> Result<T, D::Error>
where
D: Deserializer<'de>,
P: FnOnce(&str) -> Result<T, String>,
F: FnOnce(&T) -> String,
{
let input = String::deserialize(deserializer)?;
let value = parse(&input).map_err(D::Error::custom)?;
if format(&value) != input {
return Err(D::Error::custom("non-canonical V1 string payload"));
}
Ok(value)
}
#[cfg(feature = "chrono")]
#[inline]
fn serialize_canonical_vec<S, T, F>(
values: &[T],
serializer: S,
format: F,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
F: Fn(&T) -> String,
{
serializer.collect_seq(values.iter().map(format))
}
#[cfg(any(feature = "chrono", feature = "url"))]
fn deserialize_canonical_vec<'de, D, T, P, F>(
deserializer: D,
parse: P,
format: F,
) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
P: Fn(&str) -> Result<T, String>,
F: Fn(&T) -> String,
{
ValidatedSequenceVisitor::<String, T, _>::new(|input: String| {
let value = parse(&input)?;
if format(&value) != input {
return Err("non-canonical V1 string payload".to_owned());
}
Ok(value)
})
.deserialize(deserializer)
}
#[cfg(feature = "chrono")]
macro_rules! define_chrono_wire {
($scalar:ident, $vector:ident, [$($import:path),+], $type:ty, $parse:expr, $format:expr $(, $validation_error:path => $validate:expr)?) => {
pub(crate) mod $scalar {
$(use $import;)+
use serde::Deserializer;
use serde::Serializer;
$(use $validation_error as _;)?
#[inline]
pub(crate) fn serialize<S>(value: &$type, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
$(
{
($validate)(value).map_err(S::Error::custom)?;
}
)?
super::serialize_canonical(value, serializer, $format)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<$type, D::Error>
where
D: Deserializer<'de>,
{
super::deserialize_canonical(deserializer, $parse, $format)
}
}
pub(crate) mod $vector {
$(use $import;)+
use serde::Deserializer;
use serde::Serializer;
$(use $validation_error as _;)?
pub(crate) fn serialize<S>(values: &[$type], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
$(
{
for value in values {
($validate)(value).map_err(S::Error::custom)?;
}
}
)?
super::serialize_canonical_vec(values, serializer, $format)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<$type>, D::Error>
where
D: Deserializer<'de>,
{
super::deserialize_canonical_vec(deserializer, $parse, $format)
}
}
};
}
#[cfg(feature = "chrono")]
define_chrono_wire!(
date,
date_vec,
[chrono::NaiveDate],
NaiveDate,
|input| NaiveDate::parse_from_str(input, "%F").map_err(|error| error.to_string()),
|value: &NaiveDate| value.format("%F").to_string()
);
#[cfg(feature = "chrono")]
define_chrono_wire!(
time,
time_vec,
[chrono::NaiveTime],
NaiveTime,
|input| NaiveTime::parse_from_str(input, "%H:%M:%S%.f").map_err(|error| error.to_string()),
|value: &NaiveTime| value.format("%H:%M:%S%.f").to_string()
);
#[cfg(feature = "chrono")]
define_chrono_wire!(
datetime,
datetime_vec,
[chrono::NaiveDateTime],
NaiveDateTime,
|input| NaiveDateTime::parse_from_str(input, "%Y-%m-%dT%H:%M:%S%.f")
.map_err(|error| error.to_string()),
|value: &NaiveDateTime| value.format("%Y-%m-%dT%H:%M:%S%.f").to_string()
);
#[cfg(feature = "chrono")]
define_chrono_wire!(
instant,
instant_vec,
[chrono::DateTime, chrono::Datelike, chrono::SecondsFormat, chrono::Utc],
DateTime<Utc>,
|input| DateTime::parse_from_rfc3339(input)
.map(|value| value.with_timezone(&Utc))
.map_err(|error| error.to_string()),
|value: &DateTime<Utc>| value.to_rfc3339_opts(SecondsFormat::AutoSi, true),
serde::ser::Error => |value: &DateTime<Utc>| {
crate::wire::validate_instant_year(value.year())
}
);
#[cfg(feature = "url")]
macro_rules! define_url_wire {
() => {
pub(crate) mod url {
use serde::Deserializer;
use serde::Serializer;
use super::Url;
#[inline]
pub(crate) fn serialize<S>(value: &Url, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(value.as_str())
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Url, D::Error>
where
D: Deserializer<'de>,
{
super::deserialize_canonical(
deserializer,
|input| Url::parse(input).map_err(|error| error.to_string()),
|value: &Url| value.as_str().to_owned(),
)
}
}
pub(crate) mod url_vec {
use serde::Deserializer;
use serde::Serializer;
use super::Url;
#[inline]
pub(crate) fn serialize<S>(values: &[Url], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_seq(values.iter().map(|value| value.as_str()))
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Url>, D::Error>
where
D: Deserializer<'de>,
{
super::deserialize_canonical_vec(
deserializer,
|input| Url::parse(input).map_err(|error| error.to_string()),
|value: &Url| value.as_str().to_owned(),
)
}
}
};
}
#[cfg(feature = "url")]
define_url_wire!();
#[cfg(feature = "big-integer")]
macro_rules! define_decimal_serde {
($scalar_module:ident, $vector_module:ident, $type:ident) => {
pub(crate) mod $scalar_module {
use num_bigint::$type;
use serde::Deserializer;
use serde::Serializer;
use super::decimal;
#[inline]
pub(crate) fn serialize<S>(value: &$type, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
decimal::serialize(value, serializer)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<$type, D::Error>
where
D: Deserializer<'de>,
{
decimal::deserialize(deserializer)
}
}
pub(crate) mod $vector_module {
use num_bigint::$type;
use serde::Deserializer;
use serde::Serializer;
use super::decimal;
#[inline]
pub(crate) fn serialize<S>(values: &[$type], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
decimal::serialize_vec(values, serializer)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<$type>, D::Error>
where
D: Deserializer<'de>,
{
decimal::deserialize_vec(deserializer)
}
}
};
}
#[cfg(feature = "big-integer")]
define_decimal_serde!(big_integer, big_integer_vec, BigInt);
#[cfg(feature = "big-decimal")]
pub(crate) mod big_decimal {
use bigdecimal::BigDecimal;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde::de::Error as _;
use serde::ser::Error as _;
use super::BigDecimalPayload;
#[inline]
pub(crate) fn serialize<S>(value: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
BigDecimalPayload::try_from(value)
.map_err(S::Error::custom)?
.serialize(serializer)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
where
D: Deserializer<'de>,
{
BigDecimalPayload::deserialize(deserializer)?
.try_into()
.map_err(D::Error::custom)
}
}
#[cfg(feature = "big-decimal")]
pub(crate) mod big_decimal_vec {
use bigdecimal::BigDecimal;
use serde::Deserializer;
use serde::Serializer;
use serde::ser::Error as _;
use serde::ser::SerializeSeq;
use super::BigDecimalPayload;
use super::internal::ValidatedSequenceVisitor;
pub(crate) fn serialize<S>(values: &[BigDecimal], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut sequence = serializer.serialize_seq(Some(values.len()))?;
for value in values {
let payload = BigDecimalPayload::try_from(value).map_err(S::Error::custom)?;
sequence.serialize_element(&payload)?;
}
sequence.end()
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<BigDecimal>, D::Error>
where
D: Deserializer<'de>,
{
ValidatedSequenceVisitor::<BigDecimalPayload, BigDecimal, _>::new(
|value: BigDecimalPayload| value.try_into().map_err(str::to_owned),
)
.deserialize(deserializer)
}
}
pub(crate) mod duration {
use std::time::Duration;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde::de::Error as _;
use super::DurationPayload;
#[inline]
pub(crate) fn serialize<S>(value: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
DurationPayload::from(value).serialize(serializer)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
DurationPayload::deserialize(deserializer)?
.try_into()
.map_err(D::Error::custom)
}
}
pub(crate) mod duration_vec {
use std::time::Duration;
use serde::Deserializer;
use serde::Serializer;
use super::DurationPayload;
use super::internal::ValidatedSequenceVisitor;
#[inline]
pub(crate) fn serialize<S>(values: &[Duration], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_seq(values.iter().map(DurationPayload::from))
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Duration>, D::Error>
where
D: Deserializer<'de>,
{
ValidatedSequenceVisitor::<DurationPayload, Duration, _>::new(|value: DurationPayload| {
value.try_into().map_err(str::to_owned)
})
.deserialize(deserializer)
}
}
#[cfg(test)]
mod contract_boundary_tests {
#[cfg(feature = "chrono")]
#[test]
fn test_instant_year_accepts_only_the_four_digit_wire_range() {
assert!(super::validate_instant_year(0).is_ok());
assert!(super::validate_instant_year(9999).is_ok());
assert!(matches!(
super::validate_instant_year(-1),
Err(crate::ValueWireEncodeError::InstantYearOutOfRange { year: -1 }),
));
assert!(matches!(
super::validate_instant_year(10_000),
Err(crate::ValueWireEncodeError::InstantYearOutOfRange { year: 10_000 }),
));
}
#[cfg(feature = "big-decimal")]
#[test]
fn test_decimal_scale_accepts_only_the_inclusive_wire_bound() {
assert!(super::is_valid_big_decimal_scale(0));
assert!(super::is_valid_big_decimal_scale(150_000));
assert!(super::is_valid_big_decimal_scale(-150_000));
assert!(!super::is_valid_big_decimal_scale(150_001));
assert!(!super::is_valid_big_decimal_scale(-150_001));
assert!(!super::is_valid_big_decimal_scale(i64::MIN));
}
}