blob: 3b098248cdcd96ac1a54ceb665c9e63f9a10af4a [file] [log] [blame]
use chrono::*;
use diesel::query_dsl::LoadQuery;
pub use quickcheck::quickcheck;
pub use crate::schema::{connection_without_transaction, TestConnection};
#[cfg(not(feature = "sqlite"))]
pub use diesel::data_types::*;
pub use diesel::result::Error;
pub use diesel::sql_types::{HasSqlType, SingleValue, SqlType};
pub use diesel::*;
use deserialize::FromSqlRow;
use diesel::expression::{AsExpression, TypedExpressionType};
use diesel::query_builder::{AsQuery, QueryId};
#[cfg(feature = "postgres")]
use std::collections::Bound;
pub fn test_type_round_trips<ST, T, F>(value: T, cmp: F) -> bool
where
F: Fn(&T, &T) -> bool,
ST: QueryId + SqlType + TypedExpressionType + SingleValue,
<TestConnection as Connection>::Backend: HasSqlType<ST>,
T: AsExpression<ST>
+ FromSqlRow<ST, <TestConnection as Connection>::Backend>
+ Queryable<ST, <TestConnection as Connection>::Backend>
+ Clone
+ ::std::fmt::Debug
+ 'static,
for<'a> dsl::BareSelect<<T as AsExpression<ST>>::Expression>:
AsQuery + LoadQuery<'a, TestConnection, T>,
{
let connection = &mut connection_without_transaction();
let query = select(value.clone().into_sql::<ST>());
let result = query.get_result::<T>(connection);
match result {
Ok(res) => {
if cmp(&value, &res) {
println!("{value:?}, {res:?}");
false
} else {
true
}
}
Err(Error::DatabaseError(_, ref e))
if e.message() == "invalid byte sequence for encoding \"UTF8\": 0x00" =>
{
true
}
Err(e) => {
panic!("Query failed: {:?} -> value: {:?}", e, value)
}
}
}
pub fn id<A>(a: A) -> A {
a
}
pub fn ne<A: PartialEq<A>>(a: &A, b: &A) -> bool {
a != b
}
macro_rules! test_round_trip {
($test_name:ident, $sql_type:ty, $tpe:ty) => {
test_round_trip!($test_name, $sql_type, $tpe, id);
};
($test_name: ident, $sql_type: ty, $tpe: ty, $map_fn: ident) => {
test_round_trip!($test_name, $sql_type, $tpe, $map_fn, ne);
};
($test_name:ident, $sql_type:ty, $tpe:ty, $map_fn:ident, $cmp: expr) => {
#[test]
#[allow(clippy::type_complexity)]
fn $test_name() {
use diesel::sql_types::*;
fn round_trip(val: $tpe) -> bool {
test_type_round_trips::<$sql_type, _, _>($map_fn(val), $cmp)
}
fn option_round_trip(val: Option<$tpe>) -> bool {
let val = val.map($map_fn);
test_type_round_trips::<Nullable<$sql_type>, _, _>(val, |a, b| match (a, b) {
(None, None) => false,
(_, None) | (None, _) => true,
(Some(a), Some(b)) => $cmp(a, b),
})
}
quickcheck(round_trip as fn($tpe) -> bool);
quickcheck(option_round_trip as fn(Option<$tpe>) -> bool);
}
};
}
#[allow(clippy::float_cmp)]
pub fn f32_ne(a: &f32, b: &f32) -> bool {
if a.is_nan() && b.is_nan() {
false
} else {
a != b
}
}
#[allow(clippy::float_cmp)]
pub fn f64_ne(a: &f64, b: &f64) -> bool {
if a.is_nan() && b.is_nan() {
false
} else {
a != b
}
}
test_round_trip!(i16_roundtrips, SmallInt, i16);
test_round_trip!(i32_roundtrips, Integer, i32);
test_round_trip!(i64_roundtrips, BigInt, i64);
test_round_trip!(f32_roundtrips, Float, FloatWrapper, mk_f32, f32_ne);
test_round_trip!(f64_roundtrips, Double, DoubleWrapper, mk_f64, f64_ne);
test_round_trip!(string_roundtrips, VarChar, String);
test_round_trip!(text_roundtrips, Text, String);
test_round_trip!(binary_roundtrips, Binary, Vec<u8>);
test_round_trip!(bool_roundtrips, Bool, bool);
#[cfg(feature = "sqlite")]
mod sqlite_types {
use super::*;
test_round_trip!(date_roundtrips, Date, String);
test_round_trip!(time_roundtrips, Time, String);
test_round_trip!(timestamp_roundtrips, Timestamp, String);
test_round_trip!(naive_time_roundtrips, Time, (u32, u32), mk_naive_time);
test_round_trip!(naive_date_roundtrips, Date, u32, mk_naive_date);
test_round_trip!(
naive_datetime_roundtrips,
Timestamp,
(i64, u32),
mk_naive_datetime
);
}
#[cfg(feature = "postgres")]
mod pg_types {
extern crate ipnetwork;
extern crate uuid;
use super::*;
use bigdecimal::BigDecimal;
test_round_trip!(date_roundtrips, Date, PgDate);
test_round_trip!(time_roundtrips, Time, PgTime);
test_round_trip!(timestamp_roundtrips, Timestamp, PgTimestamp);
test_round_trip!(interval_roundtrips, Interval, PgInterval);
test_round_trip!(numeric_roundtrips, Numeric, PgNumeric);
test_round_trip!(money_roundtrips, Money, PgMoney);
test_round_trip!(
naive_datetime_roundtrips,
Timestamp,
(i64, u32),
mk_pg_naive_datetime
);
test_round_trip!(naive_time_roundtrips, Time, (u32, u32), mk_pg_naive_time);
test_round_trip!(naive_date_roundtrips, Date, u32, mk_naive_date);
test_round_trip!(datetime_roundtrips, Timestamptz, (i64, u32), mk_datetime);
test_round_trip!(
naive_datetime_roundtrips_tz,
Timestamptz,
(i64, u32),
mk_pg_naive_datetime
);
test_round_trip!(
uuid_roundtrips,
Uuid,
(u32, u16, u16, (u8, u8, u8, u8, u8, u8, u8, u8)),
mk_uuid
);
test_round_trip!(array_of_int_roundtrips, Array<Integer>, Vec<i32>);
test_round_trip!(array_of_bigint_roundtrips, Array<BigInt>, Vec<i64>);
test_round_trip!(array_of_dynamic_size_roundtrips, Array<Text>, Vec<String>);
test_round_trip!(
array_of_nullable_roundtrips,
Array<Nullable<Text>>,
Vec<Option<String>>
);
test_round_trip!(
macaddr_roundtrips,
MacAddr,
(u8, u8, u8, u8, u8, u8),
mk_macaddr
);
test_round_trip!(cidr_v4_roundtrips, Cidr, (u8, u8, u8, u8), mk_ipv4);
test_round_trip!(
cidr_v4_roundtrips_ipnet,
Cidr,
(u8, u8, u8, u8),
mk_ipv4_ipnet
);
test_round_trip!(
cidr_v6_roundtrips,
Cidr,
(u16, u16, u16, u16, u16, u16, u16, u16),
mk_ipv6
);
test_round_trip!(
cidr_v6_roundtrips_ipnet,
Cidr,
(u16, u16, u16, u16, u16, u16, u16, u16),
mk_ipv6_ipnet
);
test_round_trip!(inet_v4_roundtrips, Inet, (u8, u8, u8, u8), mk_ipv4);
test_round_trip!(
inet_v4_roundtrips_ipnet,
Inet,
(u8, u8, u8, u8),
mk_ipv4_ipnet
);
test_round_trip!(
inet_v6_roundtrips,
Inet,
(u16, u16, u16, u16, u16, u16, u16, u16),
mk_ipv6
);
test_round_trip!(
inet_v6_roundtrips_ipnet,
Inet,
(u16, u16, u16, u16, u16, u16, u16, u16),
mk_ipv6_ipnet
);
test_round_trip!(bigdecimal_roundtrips, Numeric, (i64, u64), mk_bigdecimal);
test_round_trip!(int4range_roundtrips, Range<Int4>, (i32, i32), mk_bounds);
test_round_trip!(int8range_roundtrips, Range<Int8>, (i64, i64), mk_bounds);
test_round_trip!(
daterange_roundtrips,
Range<Date>,
(u32, u32),
mk_date_bounds
);
test_round_trip!(
numrange_roundtrips,
Range<Numeric>,
(i64, u64, i64, u64),
mk_num_bounds
);
test_round_trip!(
tsrange_roundtrips,
Range<Timestamp>,
(i64, u32, i64, u32),
mk_ts_bounds
);
test_round_trip!(
tstzrange_roundtrips,
Range<Timestamptz>,
(i64, u32, i64, u32),
mk_tstz_bounds
);
test_round_trip!(json_roundtrips, Json, SerdeWrapper, mk_serde_json);
test_round_trip!(jsonb_roundtrips, Jsonb, SerdeWrapper, mk_serde_json);
test_round_trip!(char_roundtrips, CChar, u8);
#[allow(clippy::type_complexity)]
fn mk_uuid(data: (u32, u16, u16, (u8, u8, u8, u8, u8, u8, u8, u8))) -> self::uuid::Uuid {
let a = data.3;
let b = [a.0, a.1, a.2, a.3, a.4, a.5, a.6, a.7];
uuid::Uuid::from_fields(data.0, data.1, data.2, &b)
}
fn mk_macaddr(data: (u8, u8, u8, u8, u8, u8)) -> [u8; 6] {
[data.0, data.1, data.2, data.3, data.4, data.5]
}
fn mk_ipv4(data: (u8, u8, u8, u8)) -> ipnetwork::IpNetwork {
use std::net::Ipv4Addr;
let ip = Ipv4Addr::new(data.0, data.1, data.2, data.3);
ipnetwork::IpNetwork::V4(ipnetwork::Ipv4Network::new(ip, 32).unwrap())
}
fn mk_ipv4_ipnet(data: (u8, u8, u8, u8)) -> ipnet::IpNet {
use std::net::Ipv4Addr;
let ip = Ipv4Addr::new(data.0, data.1, data.2, data.3);
ipnet::IpNet::V4(ipnet::Ipv4Net::new(ip, 32).unwrap())
}
fn mk_ipv6(data: (u16, u16, u16, u16, u16, u16, u16, u16)) -> ipnetwork::IpNetwork {
use std::net::Ipv6Addr;
let ip = Ipv6Addr::new(
data.0, data.1, data.2, data.3, data.4, data.5, data.6, data.7,
);
ipnetwork::IpNetwork::V6(ipnetwork::Ipv6Network::new(ip, 128).unwrap())
}
fn mk_ipv6_ipnet(data: (u16, u16, u16, u16, u16, u16, u16, u16)) -> ipnet::IpNet {
use std::net::Ipv6Addr;
let ip = Ipv6Addr::new(
data.0, data.1, data.2, data.3, data.4, data.5, data.6, data.7,
);
ipnet::IpNet::V6(ipnet::Ipv6Net::new(ip, 128).unwrap())
}
fn mk_bounds<T: Ord + PartialEq>(data: (T, T)) -> (Bound<T>, Bound<T>) {
if data.0 == data.1 {
// This is invalid but we don't have a way to say that to quickcheck
return (Bound::Included(data.0), Bound::Unbounded);
}
if data.0 < data.1 {
(Bound::Included(data.0), Bound::Excluded(data.1))
} else {
(Bound::Included(data.1), Bound::Excluded(data.0))
}
}
fn mk_date_bounds(data: (u32, u32)) -> (Bound<NaiveDate>, Bound<NaiveDate>) {
let d1 = mk_naive_date(data.0);
let d2 = mk_naive_date(data.1);
mk_bounds((d1, d2))
}
fn mk_num_bounds(data: (i64, u64, i64, u64)) -> (Bound<BigDecimal>, Bound<BigDecimal>) {
let b1 = mk_bigdecimal((data.0, data.1));
let b2 = mk_bigdecimal((data.2, data.3));
mk_bounds((b1, b2))
}
fn mk_ts_bounds(data: (i64, u32, i64, u32)) -> (Bound<NaiveDateTime>, Bound<NaiveDateTime>) {
let ts1 = mk_pg_naive_datetime((data.0, data.1));
let ts2 = mk_pg_naive_datetime((data.2, data.3));
mk_bounds((ts1, ts2))
}
fn mk_tstz_bounds(data: (i64, u32, i64, u32)) -> (Bound<DateTime<Utc>>, Bound<DateTime<Utc>>) {
let tstz1 = mk_datetime((data.0, data.1));
let tstz2 = mk_datetime((data.2, data.3));
mk_bounds((tstz1, tstz2))
}
pub fn mk_pg_naive_datetime(data: (i64, u32)) -> NaiveDateTime {
// https://www.postgresql.org/docs/current/datatype-datetime.html
let earliest_pg_date = NaiveDate::from_ymd_opt(-4713, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 1)
.unwrap();
// chrono cannot even represent that, so don't worry for now
//let latest_pg_date = NaiveDate::from_ymd(294276, 31,12).and_hms(23,59,59);
let mut r = mk_naive_datetime(data);
loop {
if r < earliest_pg_date {
let diff = earliest_pg_date - r;
r = earliest_pg_date + diff;
} else {
break;
}
}
// Strip of nano second precision as postgres
// does not accuratly handle them
let nanos = (r.nanosecond() / 1000) * 1000;
r.with_nanosecond(nanos).unwrap()
}
pub fn mk_pg_naive_time(data: (u32, u32)) -> NaiveTime {
let time = mk_naive_time(data);
// Strip of nano second precision as postgres
// does not accuratly handle them
let nanos = (time.nanosecond() / 1000) * 1000;
time.with_nanosecond(nanos).unwrap()
}
pub fn mk_datetime(data: (i64, u32)) -> DateTime<Utc> {
Utc.from_utc_datetime(&mk_pg_naive_datetime(data))
}
}
#[cfg(feature = "mysql")]
mod mysql_types {
use super::*;
test_round_trip!(i8_roundtrips, TinyInt, i8);
test_round_trip!(
naive_datetime_roundtrips,
Timestamp,
(i64, u32),
mk_naive_timestamp
);
test_round_trip!(
naive_datetime_roundtrips_to_datetime,
Datetime,
(i64, u32),
mk_mysql_naive_datetime
);
test_round_trip!(naive_time_roundtrips, Time, (u32, u32), mk_mysql_naive_time);
test_round_trip!(naive_date_roundtrips, Date, u32, mk_naive_date);
test_round_trip!(
mysql_time_time_roundtrips,
Time,
(u32, u32),
mk_mysql_time_for_time,
cmp_mysql_time
);
test_round_trip!(
mysql_time_date_roundtrips,
Date,
u32,
mk_mysql_time_for_date,
cmp_mysql_time
);
test_round_trip!(
mysql_time_date_time_roundtrips,
Datetime,
(i64, u32),
mk_mysql_time_for_date_time,
cmp_mysql_time
);
test_round_trip!(
mysql_time_timestamp_roundtrips,
Timestamp,
(i64, u32),
mk_mysql_time_for_timestamp,
cmp_mysql_time
);
test_round_trip!(bigdecimal_roundtrips, Numeric, (i64, u64), mk_bigdecimal);
test_round_trip!(u16_roundtrips, Unsigned<SmallInt>, u16);
test_round_trip!(u32_roundtrips, Unsigned<Integer>, u32);
test_round_trip!(u64_roundtrips, Unsigned<BigInt>, u64);
test_round_trip!(json_roundtrips, Json, SerdeWrapper, mk_serde_json);
pub fn mk_mysql_time_for_time(data: (u32, u32)) -> MysqlTime {
let t = mk_mysql_naive_time(data);
MysqlTime::new(
0,
0,
0,
t.hour(),
t.minute(),
t.second(),
t.nanosecond() as _,
false,
MysqlTimestampType::MYSQL_TIMESTAMP_TIME,
0,
)
}
pub fn mk_mysql_time_for_date(data: u32) -> MysqlTime {
let date = mk_naive_date(data);
MysqlTime::new(
date.year() as _,
date.month(),
date.day(),
0,
0,
0,
0,
false,
MysqlTimestampType::MYSQL_TIMESTAMP_DATE,
0,
)
}
pub fn mk_mysql_time_for_date_time(data: (i64, u32)) -> MysqlTime {
let t = mk_mysql_naive_datetime(data);
MysqlTime::new(
t.year() as _,
t.month(),
t.day(),
t.hour(),
t.minute(),
t.second(),
t.timestamp_subsec_micros() as _,
false,
MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME,
0,
)
}
pub fn mk_mysql_time_for_timestamp(data: (i64, u32)) -> MysqlTime {
let t = mk_naive_timestamp(data);
MysqlTime::new(
t.year() as _,
t.month(),
t.day(),
t.hour(),
t.minute(),
t.second(),
t.timestamp_subsec_micros() as _,
false,
MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME,
0,
)
}
pub fn mk_mysql_naive_time(data: (u32, u32)) -> NaiveTime {
let time = mk_naive_time(data);
// Strip of nano second precision as mysql
// does not handle them at all
time.with_nanosecond(0).unwrap()
}
pub fn mk_naive_timestamp((mut seconds, nanos): (i64, u32)) -> NaiveDateTime {
// https://dev.mysql.com/doc/refman/8.0/en/datetime.html
let earliest_mysql_date = NaiveDate::from_ymd_opt(1970, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 1)
.unwrap();
let latest_mysql_date = NaiveDate::from_ymd_opt(2038, 1, 19)
.unwrap()
.and_hms_opt(3, 14, 7)
.unwrap();
if seconds != 0 {
seconds = earliest_mysql_date.timestamp()
+ ((latest_mysql_date.timestamp() - earliest_mysql_date.timestamp()) % seconds);
} else {
seconds = earliest_mysql_date.timestamp();
}
let r = mk_naive_datetime((seconds, nanos));
// Strip of nano second precision as mysql
// does not accuratly handle them
let nanos = (r.nanosecond() / 1000) * 1000;
r.with_nanosecond(nanos).unwrap()
}
pub fn mk_mysql_naive_datetime(data: (i64, u32)) -> NaiveDateTime {
// https://dev.mysql.com/doc/refman/8.0/en/datetime.html
let earliest_mysql_date = NaiveDate::from_ymd_opt(1000, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 1)
.unwrap();
let latest_mysql_date = NaiveDate::from_ymd_opt(9999, 12, 31)
.unwrap()
.and_hms_opt(23, 59, 59)
.unwrap();
let mut r = mk_naive_datetime(data);
loop {
if r < earliest_mysql_date {
let diff = earliest_mysql_date - r;
r = earliest_mysql_date + diff;
} else if r > latest_mysql_date {
let diff = r - latest_mysql_date;
r = earliest_mysql_date + diff;
} else {
break;
}
}
// Strip of nano second precision as mysql
// does not accuratly handle them
let nanos = (r.nanosecond() / 1000) * 1000;
r.with_nanosecond(nanos).unwrap()
}
fn cmp_mysql_time(this: &MysqlTime, other: &MysqlTime) -> bool {
let is_eq = this.year == other.year
&& this.month == other.month
&& this.day == other.day
&& this.hour == other.hour
&& this.minute == other.minute
&& this.second == other.second
&& this.second_part == other.second_part
&& this.neg == other.neg
&& this.time_zone_displacement == other.time_zone_displacement;
!is_eq
}
}
pub fn mk_naive_datetime((mut secs, mut nano): (i64, u32)) -> NaiveDateTime {
const MAX_SECONDS: i64 = 86_400;
const MAX_NANO: u32 = 2_000_000_000;
const MAX_YEAR: i64 = (i32::MAX >> 13) as i64;
const MIN_YEAR: i64 = (i32::MIN >> 13) as i64;
nano /= 1000;
loop {
let days = secs / MAX_SECONDS;
let seconds = secs - (days * MAX_SECONDS);
let years = (days + 719_163) / 365;
if days + 719_163 > i32::MAX as i64 {
secs -= ((days + 719_163) - i32::MAX as i64) * MAX_SECONDS;
continue;
}
if years >= MAX_YEAR {
secs -= (((years - MAX_YEAR) * 365 - 719_163) * MAX_SECONDS).abs();
continue;
}
if years <= MIN_YEAR {
secs += (((years - MIN_YEAR) * 365 - 719_163) * MAX_SECONDS).abs();
continue;
}
if seconds >= MAX_SECONDS {
secs -= MAX_SECONDS;
continue;
}
if nano >= MAX_NANO {
nano -= MAX_NANO;
continue;
}
break;
}
NaiveDateTime::from_timestamp_opt(secs, nano).unwrap()
}
pub fn mk_naive_time((mut seconds, mut nano): (u32, u32)) -> NaiveTime {
const MAX_SECONDS: u32 = 86_400;
const MAX_NANO: u32 = 2_000_000_000;
nano /= 1000;
loop {
match (seconds >= MAX_SECONDS, nano >= MAX_NANO) {
(true, true) => {
seconds -= MAX_SECONDS;
nano -= MAX_NANO;
}
(true, false) => {
seconds -= MAX_SECONDS;
}
(false, true) => nano -= MAX_NANO,
(false, false) => break,
}
}
NaiveTime::from_num_seconds_from_midnight_opt(seconds, nano).unwrap()
}
#[cfg(any(feature = "postgres", feature = "mysql"))]
fn mk_bigdecimal(data: (i64, u64)) -> bigdecimal::BigDecimal {
format!("{}.{}", data.0, data.1)
.parse()
.expect("Could not interpret as bigdecimal")
}
#[cfg(feature = "postgres")]
pub fn mk_naive_date(days: u32) -> NaiveDate {
let earliest_pg_date = NaiveDate::from_ymd_opt(-4713, 11, 24).unwrap();
let latest_chrono_date = NaiveDate::MAX;
let num_days_representable = latest_chrono_date
.signed_duration_since(earliest_pg_date)
.num_days();
earliest_pg_date + Duration::days(days as i64 % num_days_representable)
}
#[cfg(feature = "mysql")]
pub fn mk_naive_date(days: u32) -> NaiveDate {
let earliest_mysql_date = NaiveDate::from_ymd_opt(1000, 1, 1).unwrap();
let latest_mysql_date = NaiveDate::from_ymd_opt(9999, 12, 31).unwrap();
let num_days_representable = latest_mysql_date
.signed_duration_since(earliest_mysql_date)
.num_days();
earliest_mysql_date + Duration::days(days as i64 % num_days_representable)
}
#[cfg(feature = "sqlite")]
pub fn mk_naive_date(days: u32) -> NaiveDate {
let earliest_sqlite_date = NaiveDate::MIN;
let latest_sqlite_date = NaiveDate::MAX;
let num_days_representable = latest_sqlite_date
.signed_duration_since(earliest_sqlite_date)
.num_days();
earliest_sqlite_date + Duration::days(days as i64 % num_days_representable)
}
#[derive(Debug, Clone, Copy)]
struct FloatWrapper(f32);
fn mk_f32(f: FloatWrapper) -> f32 {
f.0
}
impl quickcheck::Arbitrary for FloatWrapper {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut f = f32::arbitrary(g);
while (cfg!(feature = "sqlite") || cfg!(feature = "mysql")) && f.is_nan() {
f = f32::arbitrary(g);
}
FloatWrapper(f)
}
}
#[derive(Debug, Clone, Copy)]
struct DoubleWrapper(f64);
fn mk_f64(f: DoubleWrapper) -> f64 {
f.0
}
impl quickcheck::Arbitrary for DoubleWrapper {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut f = f64::arbitrary(g);
while (cfg!(feature = "sqlite") || cfg!(featuer = "mysql")) && f.is_nan() {
f = f64::arbitrary(g);
}
DoubleWrapper(f)
}
}
#[cfg(any(feature = "postgres", feature = "mysql"))]
#[derive(Clone, Debug)]
struct SerdeWrapper(serde_json::Value);
#[cfg(any(feature = "postgres", feature = "mysql"))]
impl quickcheck::Arbitrary for SerdeWrapper {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
SerdeWrapper(arbitrary_serde(g, 0))
}
}
#[cfg(any(feature = "postgres", feature = "mysql"))]
fn arbitrary_serde(g: &mut quickcheck::Gen, depth: usize) -> serde_json::Value {
use quickcheck::Arbitrary;
let variant = if depth > 1 {
&[0, 1, 2, 3] as &[_]
} else {
&[0, 1, 2, 3, 4, 5] as &[_]
};
match g.choose(variant).expect("Slice is not empty") {
0 => serde_json::Value::Null,
1 => serde_json::Value::Bool(bool::arbitrary(g)),
2 => {
// don't use floats here
// comparing floats is complicated
let n = i32::arbitrary(g);
serde_json::Value::Number(n.into())
}
3 => {
let s = String::arbitrary(g)
.chars()
.filter(|c| c.is_ascii_alphabetic())
.collect();
serde_json::Value::String(s)
}
4 => {
let len = g
.choose(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15_i8])
.expect("Slice is not empty");
let values = (0..*len).map(|_| arbitrary_serde(g, depth + 1)).collect();
serde_json::Value::Array(values)
}
5 => {
let fields = g
.choose(&[0, 1, 2, 3, 4, 5, 6_i8])
.expect("Slice is not empty");
let map = (0..*fields)
.map(|_| {
let len = g.choose(&[1, 2, 3, 4, 5]).expect("Slice is not empty");
let name = String::arbitrary(g)
.chars()
.filter(|c| c.is_ascii_alphabetic())
.take(*len)
.collect();
(name, arbitrary_serde(g, depth + 1))
})
.collect();
serde_json::Value::Object(map)
}
_ => unimplemented!(),
}
}
#[cfg(any(feature = "postgres", feature = "mysql"))]
fn mk_serde_json(data: SerdeWrapper) -> serde_json::Value {
data.0
}
#[cfg(feature = "postgres")]
mod unstable_types {
use super::{ne, quickcheck, test_type_round_trips};
use std::time::*;
fn strip_nanosecond_precision(time: SystemTime) -> SystemTime {
match time.duration_since(UNIX_EPOCH) {
Ok(duration) => time - Duration::new(0, duration.subsec_nanos() % 1000),
Err(e) => time + Duration::new(0, e.duration().subsec_nanos() % 1000),
}
}
test_round_trip!(
systemtime_roundtrips,
Timestamp,
SystemTime,
strip_nanosecond_precision
);
}