2018-07-16 19:46:10 +00:00
|
|
|
// Copyright (C) 2018 François Laignel <fengalin@free.fr>
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use glib;
|
2019-08-19 13:32:40 +00:00
|
|
|
use glib::{Date, StaticType, ToValue};
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
use num_rational::Rational32;
|
|
|
|
|
|
|
|
use serde::de;
|
|
|
|
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
|
|
|
|
use serde::ser;
|
2018-07-22 14:55:24 +00:00
|
|
|
use serde::ser::{Serialize, SerializeTuple, Serializer};
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
use std::{fmt, mem};
|
|
|
|
|
2020-01-22 17:38:13 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
|
2018-12-25 00:15:43 +00:00
|
|
|
use Buffer;
|
2018-07-16 19:46:10 +00:00
|
|
|
use DateTime;
|
|
|
|
use Sample;
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
use date_time_serde;
|
2018-07-16 19:46:10 +00:00
|
|
|
use value::*;
|
|
|
|
|
|
|
|
fn get_other_type_id<T: StaticType>() -> usize {
|
|
|
|
match T::static_type() {
|
|
|
|
glib::Type::Other(type_id) => type_id,
|
|
|
|
type_ => panic!("Expecting `Other` variant, found `{}`", type_),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 17:38:13 +00:00
|
|
|
pub(crate) static ARRAY_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Array>);
|
|
|
|
pub(crate) static BITMASK_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Bitmask>);
|
|
|
|
pub(crate) static DATE_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Date>);
|
|
|
|
pub(crate) static DATE_TIME_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<DateTime>);
|
|
|
|
pub(crate) static FRACTION_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Fraction>);
|
|
|
|
pub(crate) static FRACTION_RANGE_OTHER_TYPE_ID: Lazy<usize> =
|
|
|
|
Lazy::new(get_other_type_id::<FractionRange>);
|
|
|
|
pub(crate) static INT_RANGE_I32_OTHER_TYPE_ID: Lazy<usize> =
|
|
|
|
Lazy::new(get_other_type_id::<IntRange<i32>>);
|
|
|
|
pub(crate) static INT_RANGE_I64_OTHER_TYPE_ID: Lazy<usize> =
|
|
|
|
Lazy::new(get_other_type_id::<IntRange<i64>>);
|
|
|
|
pub(crate) static LIST_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<List>);
|
|
|
|
pub(crate) static SAMPLE_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Sample>);
|
|
|
|
pub(crate) static BUFFER_OTHER_TYPE_ID: Lazy<usize> = Lazy::new(get_other_type_id::<Buffer>);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
impl<'a> Serialize for Fraction {
|
|
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
|
|
self.0.serialize(serializer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Fraction {
|
|
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2018-07-16 19:46:10 +00:00
|
|
|
Rational32::deserialize(deserializer)
|
2018-07-28 15:23:45 +00:00
|
|
|
.map(|rational| Fraction::new(*rational.numer(), *rational.denom()))
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
macro_rules! ser_some_value (
|
|
|
|
($value:expr, $t:ty, $ser_closure:expr) => (
|
|
|
|
{
|
|
|
|
let value = $value.get_some::<$t>().expect("ser_some_value macro");
|
|
|
|
$ser_closure(stringify!($t), value)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
macro_rules! ser_opt_value (
|
2018-07-29 20:03:48 +00:00
|
|
|
($value:expr, $t:ty, $ser_closure:expr) => (
|
2018-07-16 19:46:10 +00:00
|
|
|
{
|
2019-08-16 22:55:09 +00:00
|
|
|
let value = $value.get::<$t>().expect("ser_opt_value macro");
|
2018-07-29 20:03:48 +00:00
|
|
|
$ser_closure(stringify!($t), value)
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|
|
|
|
);
|
2019-08-16 22:55:09 +00:00
|
|
|
);
|
|
|
|
macro_rules! ser_value (
|
2018-07-16 19:46:10 +00:00
|
|
|
($value:expr, $ser_closure:expr) => (
|
|
|
|
match $value.type_() {
|
2019-08-16 22:55:09 +00:00
|
|
|
glib::Type::I8 => ser_some_value!($value, i8, $ser_closure),
|
|
|
|
glib::Type::U8 => ser_some_value!($value, u8, $ser_closure),
|
|
|
|
glib::Type::Bool => ser_some_value!($value, bool, $ser_closure),
|
|
|
|
glib::Type::I32 => ser_some_value!($value, i32, $ser_closure),
|
|
|
|
glib::Type::U32 => ser_some_value!($value, u32, $ser_closure),
|
|
|
|
glib::Type::I64 => ser_some_value!($value, i64, $ser_closure),
|
|
|
|
glib::Type::U64 => ser_some_value!($value, u64, $ser_closure),
|
|
|
|
glib::Type::F32 => ser_some_value!($value, f32, $ser_closure),
|
|
|
|
glib::Type::F64 => ser_some_value!($value, f64, $ser_closure),
|
|
|
|
glib::Type::String => ser_opt_value!($value, String, $ser_closure),
|
2018-07-16 19:46:10 +00:00
|
|
|
glib::Type::Other(type_id) => {
|
|
|
|
if *ARRAY_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, Array, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *BITMASK_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, Bitmask, $ser_closure)
|
2019-08-19 13:32:40 +00:00
|
|
|
} else if *DATE_OTHER_TYPE_ID == type_id {
|
|
|
|
ser_opt_value!($value, Date, |type_, value: Option<Date>| {
|
|
|
|
// Need to wrap the `glib::Date` in new type `date_time_serde::Date` first
|
|
|
|
// See comment in `date_time_serde.rs`
|
|
|
|
$ser_closure(type_, value.map(date_time_serde::Date::from))
|
|
|
|
})
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *DATE_TIME_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_opt_value!($value, DateTime, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *FRACTION_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, Fraction, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *FRACTION_RANGE_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, FractionRange, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *INT_RANGE_I32_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, IntRange<i32>, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *INT_RANGE_I64_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, IntRange<i64>, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *LIST_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_some_value!($value, List, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else if *SAMPLE_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_opt_value!($value, Sample, $ser_closure)
|
2018-12-25 00:15:43 +00:00
|
|
|
} else if *BUFFER_OTHER_TYPE_ID == type_id {
|
2019-08-16 22:55:09 +00:00
|
|
|
ser_opt_value!($value, Buffer, $ser_closure)
|
2018-07-16 19:46:10 +00:00
|
|
|
} else {
|
|
|
|
Err(
|
|
|
|
ser::Error::custom(
|
|
|
|
format!("unimplemented `Value` serialization for type {}",
|
|
|
|
glib::Type::Other(type_id),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
type_ => {
|
|
|
|
Err(
|
|
|
|
ser::Error::custom(
|
|
|
|
format!("unimplemented `Value` serialization for type {}", type_)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2018-07-29 20:03:48 +00:00
|
|
|
);
|
2018-07-16 19:46:10 +00:00
|
|
|
);
|
|
|
|
|
2018-10-11 08:25:28 +00:00
|
|
|
#[repr(C)]
|
2018-07-16 19:46:10 +00:00
|
|
|
pub(crate) struct SendValue(glib::SendValue);
|
|
|
|
impl SendValue {
|
|
|
|
pub(crate) fn from(send_value: glib::SendValue) -> Self {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2018-07-16 19:46:10 +00:00
|
|
|
SendValue(send_value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SendValue> for glib::SendValue {
|
|
|
|
fn from(send_value: SendValue) -> Self {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2018-07-16 19:46:10 +00:00
|
|
|
send_value.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for SendValue {
|
|
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
|
|
ser_value!(self.0, |type_, value| {
|
|
|
|
let mut tup = serializer.serialize_tuple(2)?;
|
|
|
|
tup.serialize_element(type_)?;
|
|
|
|
tup.serialize_element(&value)?;
|
|
|
|
tup.end()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_ser_send_value_collection (
|
|
|
|
($t:ident) => (
|
|
|
|
impl<'a> Serialize for $t<'a> {
|
|
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
|
|
let send_value_vec = unsafe {
|
2018-10-11 08:25:28 +00:00
|
|
|
&*(self.as_slice() as *const [glib::SendValue] as *const [SendValue])
|
2018-07-16 19:46:10 +00:00
|
|
|
};
|
|
|
|
send_value_vec.serialize(serializer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
impl_ser_send_value_collection!(Array);
|
|
|
|
impl_ser_send_value_collection!(List);
|
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
macro_rules! de_some_send_value(
|
|
|
|
($type_name:expr, $seq:expr, $t:ty) => (
|
|
|
|
de_some_send_value!("Value", $type_name, $seq, $t)
|
|
|
|
);
|
|
|
|
($outer_type:expr, $type_name:expr, $seq:expr, $t:ty) => (
|
|
|
|
de_send_value!($outer_type, $type_name, $seq, $t, $t)
|
2018-07-16 19:46:10 +00:00
|
|
|
);
|
|
|
|
);
|
2019-08-16 22:55:09 +00:00
|
|
|
macro_rules! de_opt_send_value(
|
2018-07-16 19:46:10 +00:00
|
|
|
($type_name:expr, $seq:expr, $t:ty) => (
|
2019-08-16 22:55:09 +00:00
|
|
|
de_opt_send_value!("Value", $type_name, $seq, $t)
|
2018-07-17 20:24:41 +00:00
|
|
|
);
|
|
|
|
($outer_type:expr, $type_name:expr, $seq:expr, $t:ty) => (
|
2019-08-16 22:55:09 +00:00
|
|
|
de_send_value!($outer_type, $type_name, $seq, Option<$t>, $t)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
macro_rules! de_send_value(
|
|
|
|
($outer_type:expr, $type_name:expr, $seq:expr, $elem_t:ty, $t:ty) => (
|
|
|
|
Ok(match $seq.next_element::<$elem_t>()? {
|
|
|
|
Some(base_value) => {
|
|
|
|
Some(SendValue::from(base_value
|
|
|
|
.to_value()
|
2018-07-17 20:24:41 +00:00
|
|
|
.try_into_send_value::<$t>()
|
|
|
|
.map_err(|_|
|
|
|
|
de::Error::custom(format!(
|
|
|
|
"Failed to convert `{}` with type {:?} to `SendValue`",
|
|
|
|
$outer_type,
|
|
|
|
$type_name,
|
|
|
|
))
|
2019-08-16 22:55:09 +00:00
|
|
|
)?
|
|
|
|
))
|
2018-07-17 20:24:41 +00:00
|
|
|
}
|
2019-08-16 22:55:09 +00:00
|
|
|
None => None
|
|
|
|
})
|
2018-07-16 19:46:10 +00:00
|
|
|
);
|
|
|
|
($type_name:expr, $seq:expr) => (
|
|
|
|
match $type_name.as_str() {
|
2019-08-16 22:55:09 +00:00
|
|
|
"i8" => de_some_send_value!($type_name, $seq, i8),
|
|
|
|
"u8" => de_some_send_value!($type_name, $seq, u8),
|
|
|
|
"bool" => de_some_send_value!($type_name, $seq, bool),
|
|
|
|
"i32" => de_some_send_value!($type_name, $seq, i32),
|
|
|
|
"u32" => de_some_send_value!($type_name, $seq, u32),
|
|
|
|
"i64" => de_some_send_value!($type_name, $seq, i64),
|
|
|
|
"u64" => de_some_send_value!($type_name, $seq, u64),
|
|
|
|
"f32" => de_some_send_value!($type_name, $seq, f32),
|
|
|
|
"f64" => de_some_send_value!($type_name, $seq, f64),
|
|
|
|
"String" => de_opt_send_value!($type_name, $seq, String),
|
|
|
|
"Array" => de_some_send_value!($type_name, $seq, Array),
|
|
|
|
"Bitmask" => de_some_send_value!($type_name, $seq, Bitmask),
|
2019-08-19 13:32:40 +00:00
|
|
|
"Date" => {
|
|
|
|
// Need to deserialize as `date_time_serde::Date` new type
|
|
|
|
// See comment in `date_time_serde.rs`
|
|
|
|
de_send_value!(
|
|
|
|
"Value",
|
|
|
|
$type_name,
|
|
|
|
$seq,
|
|
|
|
Option<date_time_serde::Date>,
|
|
|
|
Date
|
|
|
|
)
|
|
|
|
}
|
2019-08-16 22:55:09 +00:00
|
|
|
"DateTime" => de_opt_send_value!($type_name, $seq, DateTime),
|
|
|
|
"Fraction" => de_some_send_value!($type_name, $seq, Fraction),
|
|
|
|
"FractionRange" => de_some_send_value!($type_name, $seq, FractionRange),
|
|
|
|
"IntRange<i32>" => de_some_send_value!($type_name, $seq, IntRange<i32>),
|
|
|
|
"IntRange<i64>" => de_some_send_value!($type_name, $seq, IntRange<i64>),
|
|
|
|
"Sample" => de_opt_send_value!($type_name, $seq, Sample),
|
|
|
|
"Buffer" => de_opt_send_value!($type_name, $seq, Buffer),
|
2018-07-16 19:46:10 +00:00
|
|
|
_ => return Err(
|
|
|
|
de::Error::custom(
|
|
|
|
format!(
|
|
|
|
"unimplemented deserialization for `Value` with type `{}`",
|
|
|
|
$type_name,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
struct SendValueVisitor;
|
|
|
|
impl<'de> Visitor<'de> for SendValueVisitor {
|
|
|
|
type Value = SendValue;
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
formatter.write_str("a tuple: (name, value)")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
2018-07-22 14:55:24 +00:00
|
|
|
let type_name = seq
|
|
|
|
.next_element::<String>()?
|
2018-10-11 08:25:28 +00:00
|
|
|
.ok_or_else(|| de::Error::custom("Expected a value for `Value` type"))?;
|
2018-07-17 20:24:41 +00:00
|
|
|
let send_value = de_send_value!(type_name, seq)?
|
2018-10-11 08:25:28 +00:00
|
|
|
.ok_or_else(|| de::Error::custom("Expected a value for `Value`"))?;
|
2018-07-17 20:24:41 +00:00
|
|
|
Ok(send_value)
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for SendValue {
|
|
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2018-07-22 14:55:24 +00:00
|
|
|
deserializer.deserialize_tuple(2, SendValueVisitor {})
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_de_send_value_collection (
|
|
|
|
($t:ident) => {
|
|
|
|
impl<'a, 'de> Deserialize<'de> for $t<'a> {
|
|
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2018-07-16 19:46:10 +00:00
|
|
|
let send_value_vec = Vec::<SendValue>::deserialize(deserializer)?;
|
|
|
|
Ok($t::from_owned(unsafe{
|
|
|
|
mem::transmute::<Vec<SendValue>, Vec<glib::SendValue>>(send_value_vec)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
impl_de_send_value_collection!(Array);
|
|
|
|
impl_de_send_value_collection!(List);
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
extern crate ron;
|
|
|
|
extern crate serde_json;
|
|
|
|
|
2018-07-29 16:18:24 +00:00
|
|
|
use Array;
|
|
|
|
use Bitmask;
|
2019-08-19 13:32:40 +00:00
|
|
|
use DateTime;
|
2018-07-29 16:18:24 +00:00
|
|
|
use Fraction;
|
|
|
|
use FractionRange;
|
|
|
|
use IntRange;
|
|
|
|
use List;
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
use glib::{Date, DateMonth};
|
|
|
|
|
2018-07-16 19:46:10 +00:00
|
|
|
#[test]
|
|
|
|
fn test_serialize_simple() {
|
|
|
|
::init().unwrap();
|
|
|
|
|
|
|
|
let mut pretty_config = ron::ser::PrettyConfig::default();
|
|
|
|
pretty_config.new_line = "".to_string();
|
|
|
|
|
|
|
|
// Fraction
|
|
|
|
let fraction = Fraction::new(1, 3);
|
|
|
|
|
|
|
|
let res = ron::ser::to_string_pretty(&fraction, pretty_config.clone());
|
|
|
|
assert_eq!(Ok("(1, 3)".to_owned()), res);
|
|
|
|
|
|
|
|
let res = serde_json::to_string(&fraction).unwrap();
|
|
|
|
assert_eq!("[1,3]".to_owned(), res);
|
|
|
|
|
|
|
|
// FractionRange
|
|
|
|
let fraction_range = FractionRange::new(Fraction::new(1, 3), Fraction::new(1, 2));
|
|
|
|
|
|
|
|
let res = ron::ser::to_string_pretty(&fraction_range, pretty_config.clone());
|
2018-07-22 14:55:24 +00:00
|
|
|
assert_eq!(Ok("( min: (1, 3), max: (1, 2),)".to_owned()), res);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
let res = serde_json::to_string(&fraction_range).unwrap();
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(r#"{"min":[1,3],"max":[1,2]}"#.to_owned(), res);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
// IntRange
|
2020-06-11 08:53:35 +00:00
|
|
|
let int_range = IntRange::<i32>::with_step(0, 42, 21);
|
2018-07-16 19:46:10 +00:00
|
|
|
let res = ron::ser::to_string_pretty(&int_range, pretty_config.clone());
|
2018-07-22 14:55:24 +00:00
|
|
|
assert_eq!(Ok("( min: 0, max: 42, step: 21,)".to_owned()), res,);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
let res = serde_json::to_string(&int_range).unwrap();
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(r#"{"min":0,"max":42,"step":21}"#.to_owned(), res);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
// Bitmask
|
|
|
|
let bitmask = Bitmask::new(1024 + 128 + 32);
|
|
|
|
|
2019-12-22 07:59:23 +00:00
|
|
|
let res = ron::ser::to_string_pretty(&bitmask, pretty_config);
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(Ok("(1184)".to_owned()), res);
|
|
|
|
|
|
|
|
let res = serde_json::to_string(&bitmask).unwrap();
|
|
|
|
assert_eq!("1184".to_owned(), res);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serialize_collections() {
|
|
|
|
use Fraction;
|
|
|
|
use List;
|
|
|
|
|
|
|
|
::init().unwrap();
|
|
|
|
|
|
|
|
let mut pretty_config = ron::ser::PrettyConfig::default();
|
|
|
|
pretty_config.new_line = "".to_string();
|
|
|
|
|
|
|
|
// Array
|
2020-10-20 13:14:10 +00:00
|
|
|
let value_13 = Fraction::new(1, 3);
|
|
|
|
let value_12 = Fraction::new(1, 2);
|
|
|
|
let value_str = "test str";
|
|
|
|
let value_str_none: Option<&str> = None;
|
|
|
|
let value_date = Date::new_dmy(19, DateMonth::August, 2019);
|
|
|
|
let value_date_none: Option<Date> = None;
|
2019-08-19 13:32:40 +00:00
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
let array = Array::new(&[
|
2020-10-20 13:14:10 +00:00
|
|
|
&value_13,
|
|
|
|
&value_12,
|
|
|
|
&value_str,
|
|
|
|
&value_str_none,
|
|
|
|
&value_date,
|
|
|
|
&value_date_none,
|
2019-08-16 22:55:09 +00:00
|
|
|
]);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
|
|
|
let res = ron::ser::to_string_pretty(&array, pretty_config.clone());
|
|
|
|
assert_eq!(
|
2018-07-22 14:55:24 +00:00
|
|
|
Ok(concat!(
|
2019-08-19 13:32:40 +00:00
|
|
|
r#"["#,
|
|
|
|
r#" ("Fraction", (1, 3)),"#,
|
|
|
|
r#" ("Fraction", (1, 2)),"#,
|
|
|
|
r#" ("String", Some("test str")),"#,
|
|
|
|
r#" ("String", None),"#,
|
|
|
|
r#" ("Date", Some(YMD(2019, 8, 19))),"#,
|
|
|
|
r#" ("Date", None),"#,
|
|
|
|
r#"]"#
|
2018-10-08 12:02:23 +00:00
|
|
|
)
|
|
|
|
.to_owned()),
|
2018-07-16 19:46:10 +00:00
|
|
|
res,
|
|
|
|
);
|
|
|
|
|
|
|
|
let res = serde_json::to_string(&array).unwrap();
|
|
|
|
assert_eq!(
|
2019-08-19 13:32:40 +00:00
|
|
|
r#"[["Fraction",[1,3]],["Fraction",[1,2]],["String","test str"],["String",null],["Date",{"YMD":[2019,8,19]}],["Date",null]]"#
|
2019-08-16 22:55:09 +00:00
|
|
|
.to_owned(),
|
2018-07-16 19:46:10 +00:00
|
|
|
res
|
|
|
|
);
|
|
|
|
|
|
|
|
// List
|
2020-10-20 13:14:10 +00:00
|
|
|
let value_12 = Fraction::new(1, 2);
|
|
|
|
let value_str = "test str";
|
|
|
|
let value_str_none: Option<&str> = None;
|
|
|
|
let value_date_time = DateTime::new(2f32, 2019, 8, 19, 13, 34, 42f64).unwrap();
|
|
|
|
let value_date_time_none: Option<DateTime> = None;
|
2019-08-19 13:32:40 +00:00
|
|
|
|
|
|
|
let list = List::new(&[
|
2020-10-20 13:14:10 +00:00
|
|
|
&value_12,
|
|
|
|
&value_str,
|
|
|
|
&value_str_none,
|
|
|
|
&value_date_time,
|
|
|
|
&value_date_time_none,
|
2019-08-19 13:32:40 +00:00
|
|
|
]);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-12-22 07:59:23 +00:00
|
|
|
let res = ron::ser::to_string_pretty(&list, pretty_config);
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(
|
2018-07-22 14:55:24 +00:00
|
|
|
Ok(concat!(
|
2019-08-19 13:32:40 +00:00
|
|
|
r#"["#,
|
|
|
|
r#" ("Fraction", (1, 2)),"#,
|
|
|
|
r#" ("String", Some("test str")),"#,
|
|
|
|
r#" ("String", None),"#,
|
|
|
|
r#" ("DateTime", Some(YMDhmsTz(2019, 8, 19, 13, 34, 42, 2))),"#,
|
|
|
|
r#" ("DateTime", None),"#,
|
|
|
|
r#"]"#
|
2018-10-08 12:02:23 +00:00
|
|
|
)
|
|
|
|
.to_owned()),
|
2018-07-16 19:46:10 +00:00
|
|
|
res,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_deserialize_simple() {
|
|
|
|
::init().unwrap();
|
|
|
|
|
|
|
|
// Fraction
|
|
|
|
let fraction_ron = "(1, 3)";
|
|
|
|
let fraction: Fraction = ron::de::from_str(fraction_ron).unwrap();
|
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &3);
|
|
|
|
|
|
|
|
let fraction_json = "[1,3]";
|
|
|
|
let fraction: Fraction = serde_json::from_str(fraction_json).unwrap();
|
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &3);
|
|
|
|
|
|
|
|
// FractionRange
|
|
|
|
let fraction_range_ron = "(min: (1, 3), max: (1, 2))";
|
|
|
|
let fraction_range: FractionRange = ron::de::from_str(fraction_range_ron).unwrap();
|
|
|
|
assert_eq!(fraction_range.min().0.denom(), &3);
|
|
|
|
assert_eq!(fraction_range.max().0.denom(), &2);
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
let fraction_range_json = r#"{"min":[1,3],"max":[1,2]}"#;
|
2018-07-16 19:46:10 +00:00
|
|
|
let fraction_range: FractionRange = serde_json::from_str(fraction_range_json).unwrap();
|
|
|
|
assert_eq!(fraction_range.min().0.denom(), &3);
|
|
|
|
assert_eq!(fraction_range.max().0.denom(), &2);
|
|
|
|
|
|
|
|
// IntRange
|
|
|
|
let int_range_ron = "(min: 0, max: 42, step: 21)";
|
|
|
|
let int_range: IntRange<i32> = ron::de::from_str(int_range_ron).unwrap();
|
|
|
|
assert_eq!(int_range.min(), 0);
|
|
|
|
assert_eq!(int_range.max(), 42);
|
|
|
|
assert_eq!(int_range.step(), 21);
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
let int_range_json = r#"{"min":0,"max":42,"step":21}"#;
|
2018-07-16 19:46:10 +00:00
|
|
|
let int_range: IntRange<i32> = serde_json::from_str(int_range_json).unwrap();
|
|
|
|
assert_eq!(int_range.min(), 0);
|
|
|
|
assert_eq!(int_range.max(), 42);
|
|
|
|
assert_eq!(int_range.step(), 21);
|
|
|
|
|
|
|
|
// Bitmask
|
|
|
|
let bitmask_ref = Bitmask::new(1024 + 128 + 32);
|
|
|
|
|
|
|
|
let bitmask_ron = "(1184)";
|
|
|
|
let bitmask: Bitmask = ron::de::from_str(bitmask_ron).unwrap();
|
|
|
|
assert_eq!(bitmask_ref, bitmask);
|
|
|
|
|
|
|
|
let bitmask_json = "1184";
|
|
|
|
let bitmask: Bitmask = serde_json::from_str(bitmask_json).unwrap();
|
|
|
|
assert_eq!(bitmask_ref, bitmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-07-29 16:18:24 +00:00
|
|
|
fn test_serde_roundtrip_simple() {
|
|
|
|
::init().unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2018-07-29 16:18:24 +00:00
|
|
|
// Fraction
|
|
|
|
let fraction = Fraction::new(1, 3);
|
|
|
|
let fraction_ser = ron::ser::to_string(&fraction).unwrap();
|
|
|
|
let fraction_de: Fraction = ron::de::from_str(fraction_ser.as_str()).unwrap();
|
|
|
|
assert_eq!(fraction_de.0.numer(), fraction.0.numer());
|
|
|
|
assert_eq!(fraction_de.0.denom(), fraction.0.denom());
|
|
|
|
|
|
|
|
// FractionRange
|
|
|
|
let fraction_range = FractionRange::new(Fraction::new(1, 3), Fraction::new(1, 2));
|
|
|
|
let fraction_range_ser = ron::ser::to_string(&fraction_range).unwrap();
|
|
|
|
let fraction_range_de: FractionRange =
|
|
|
|
ron::de::from_str(fraction_range_ser.as_str()).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
fraction_range_de.min().0.denom(),
|
|
|
|
fraction_range.min().0.denom()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
fraction_range_de.max().0.denom(),
|
|
|
|
fraction_range.max().0.denom()
|
|
|
|
);
|
|
|
|
|
|
|
|
// IntRange
|
2020-06-11 08:53:35 +00:00
|
|
|
let int_range = IntRange::<i32>::with_step(0, 42, 21);
|
2018-07-29 16:18:24 +00:00
|
|
|
let int_range_ser = ron::ser::to_string(&int_range).unwrap();
|
|
|
|
let int_range_de: IntRange<i32> = ron::de::from_str(int_range_ser.as_str()).unwrap();
|
|
|
|
assert_eq!(int_range_de.min(), int_range.min());
|
|
|
|
assert_eq!(int_range_de.max(), int_range.max());
|
|
|
|
assert_eq!(int_range_de.step(), int_range.step());
|
|
|
|
|
|
|
|
// Bitmask
|
|
|
|
let bitmask = Bitmask::new(1024 + 128 + 32);
|
|
|
|
let bitmask_ser = ron::ser::to_string(&bitmask).unwrap();
|
|
|
|
let bitmask_de: Bitmask = ron::de::from_str(bitmask_ser.as_str()).unwrap();
|
|
|
|
assert_eq!(bitmask_de, bitmask);
|
|
|
|
}
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-12-22 07:59:23 +00:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2018-07-29 16:18:24 +00:00
|
|
|
#[test]
|
|
|
|
fn test_deserialize_collections() {
|
2018-07-16 19:46:10 +00:00
|
|
|
::init().unwrap();
|
|
|
|
|
|
|
|
// Array
|
2018-07-22 14:55:24 +00:00
|
|
|
let array_ron = r#"[
|
2018-07-16 19:46:10 +00:00
|
|
|
("Fraction", (1, 3)),
|
|
|
|
("Fraction", (1, 2)),
|
2019-08-16 22:55:09 +00:00
|
|
|
("String", Some("test str")),
|
|
|
|
("String", None),
|
2019-08-19 13:32:40 +00:00
|
|
|
("Date", Some(YMD(2019, 8, 19))),
|
|
|
|
("Date", None),
|
2018-07-16 19:46:10 +00:00
|
|
|
]"#;
|
|
|
|
let array: Array = ron::de::from_str(array_ron).unwrap();
|
|
|
|
let slice = array.as_slice();
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(6, slice.len());
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &3);
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction = slice[1].get::<Fraction>().expect("slice[1]").unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &2);
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
assert_eq!(
|
|
|
|
"test str".to_owned(),
|
|
|
|
slice[2].get::<String>().expect("slice[2]").unwrap()
|
|
|
|
);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(
|
|
|
|
Date::new_dmy(19, DateMonth::August, 2019),
|
|
|
|
slice[4].get::<Date>().expect("slice[4]").unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(slice[5].get::<Date>().expect("slice[5]").is_none());
|
|
|
|
|
2019-11-10 16:55:02 +00:00
|
|
|
let array_json = r#"[["Fraction",[1,3]],["Fraction",[1,2]],["String","test str"],["String",null],["Date",{"YMD":[2019,8,19]}],["Date",null]]"#;
|
2018-07-16 19:46:10 +00:00
|
|
|
let array: Array = serde_json::from_str(array_json).unwrap();
|
|
|
|
let slice = array.as_slice();
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(6, slice.len());
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &3);
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction = slice[1].get::<Fraction>().expect("slice[1]").unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &2);
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
assert_eq!(
|
|
|
|
"test str".to_owned(),
|
|
|
|
slice[2].get::<String>().expect("slice[2]").unwrap()
|
|
|
|
);
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(
|
|
|
|
Date::new_dmy(19, DateMonth::August, 2019),
|
|
|
|
slice[4].get::<Date>().expect("slice[4]").unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(slice[5].get::<Date>().expect("slice[5]").is_none());
|
|
|
|
|
2018-07-16 19:46:10 +00:00
|
|
|
// List
|
2018-07-22 14:55:24 +00:00
|
|
|
let list_ron = r#"[
|
2018-07-16 19:46:10 +00:00
|
|
|
("Fraction", (1, 2)),
|
2019-08-16 22:55:09 +00:00
|
|
|
("String", Some("test str")),
|
|
|
|
("String", None),
|
2019-08-19 13:32:40 +00:00
|
|
|
("DateTime", Some(YMDhmsTz(2019, 8, 19, 13, 34, 42, 2))),
|
|
|
|
("DateTime", None),
|
2018-07-16 19:46:10 +00:00
|
|
|
]"#;
|
|
|
|
let list: List = ron::de::from_str(list_ron).unwrap();
|
|
|
|
let slice = list.as_slice();
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(5, slice.len());
|
2018-07-16 19:46:10 +00:00
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
|
2018-07-16 19:46:10 +00:00
|
|
|
assert_eq!(fraction.0.numer(), &1);
|
|
|
|
assert_eq!(fraction.0.denom(), &2);
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
assert_eq!(
|
|
|
|
"test str".to_owned(),
|
|
|
|
slice[1].get::<String>().expect("slice[1]").unwrap()
|
|
|
|
);
|
2019-08-16 22:55:09 +00:00
|
|
|
|
|
|
|
assert!(slice[2].get::<String>().expect("slice[2]").is_none());
|
2019-08-19 13:32:40 +00:00
|
|
|
|
2019-08-21 09:06:03 +00:00
|
|
|
assert_eq!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 19, 13, 34, 42f64).unwrap(),
|
2019-08-21 09:06:03 +00:00
|
|
|
slice[3].get::<DateTime>().expect("slice[3]").unwrap()
|
|
|
|
);
|
2019-08-19 13:32:40 +00:00
|
|
|
|
|
|
|
assert!(slice[4].get::<DateTime>().expect("slice[4]").is_none());
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|
2018-07-29 16:18:24 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serde_roundtrip_collection() {
|
|
|
|
::init().unwrap();
|
|
|
|
|
|
|
|
// Array
|
2020-10-20 13:14:10 +00:00
|
|
|
let value_13 = Fraction::new(1, 3);
|
|
|
|
let value_12 = Fraction::new(1, 2);
|
|
|
|
let value_str = "test str";
|
|
|
|
let value_str_none: Option<&str> = None;
|
|
|
|
let value_date = Date::new_dmy(19, DateMonth::August, 2019);
|
|
|
|
let value_date_none: Option<Date> = None;
|
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
let array = Array::new(&[
|
2020-10-20 13:14:10 +00:00
|
|
|
&value_13,
|
|
|
|
&value_12,
|
|
|
|
&value_str,
|
|
|
|
&value_str_none,
|
|
|
|
&value_date,
|
|
|
|
&value_date_none,
|
2019-08-16 22:55:09 +00:00
|
|
|
]);
|
2018-07-29 16:18:24 +00:00
|
|
|
let array_ser = ron::ser::to_string(&array).unwrap();
|
|
|
|
|
|
|
|
let array_de: Array = ron::de::from_str(array_ser.as_str()).unwrap();
|
|
|
|
let slice_de = array_de.as_slice();
|
|
|
|
let slice = array.as_slice();
|
|
|
|
assert_eq!(slice_de.len(), slice.len());
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction_de = slice_de[0].get::<Fraction>().expect("slice_de[0]").unwrap();
|
|
|
|
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
|
2018-07-29 16:18:24 +00:00
|
|
|
assert_eq!(fraction_de.0.numer(), fraction.0.numer());
|
|
|
|
assert_eq!(fraction_de.0.denom(), fraction.0.denom());
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction_de = slice_de[1].get::<Fraction>().expect("slice_de[1]").unwrap();
|
|
|
|
let fraction = slice[1].get::<Fraction>().expect("slice[1]").unwrap();
|
2018-07-29 16:18:24 +00:00
|
|
|
assert_eq!(fraction_de.0.numer(), fraction.0.numer());
|
|
|
|
assert_eq!(fraction.0.denom(), fraction.0.denom());
|
|
|
|
|
|
|
|
assert_eq!(
|
2019-08-11 07:33:34 +00:00
|
|
|
slice_de[2].get::<String>().expect("slice_de[2]").unwrap(),
|
|
|
|
slice[2].get::<String>().expect("slice[2]").unwrap()
|
2018-07-29 16:18:24 +00:00
|
|
|
);
|
|
|
|
|
2019-08-16 22:55:09 +00:00
|
|
|
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
assert_eq!(
|
|
|
|
slice_de[4].get::<Date>().expect("slice_de[4]").unwrap(),
|
|
|
|
slice[4].get::<Date>().expect("slice[4]").unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(slice[5].get::<Date>().expect("slice[5]").is_none());
|
|
|
|
|
2018-07-29 16:18:24 +00:00
|
|
|
// List
|
2020-10-20 13:14:10 +00:00
|
|
|
let value_12 = Fraction::new(1, 2);
|
|
|
|
let value_str = "test str";
|
|
|
|
let value_str_none: Option<&str> = None;
|
|
|
|
let value_date_time = DateTime::new(2f32, 2019, 8, 19, 13, 34, 42f64).unwrap();
|
|
|
|
let value_date_time_none: Option<DateTime> = None;
|
|
|
|
|
2019-08-19 13:32:40 +00:00
|
|
|
let list = List::new(&[
|
2020-10-20 13:14:10 +00:00
|
|
|
&value_12,
|
|
|
|
&value_str,
|
|
|
|
&value_str_none,
|
|
|
|
&value_date_time,
|
|
|
|
&value_date_time_none,
|
2019-08-19 13:32:40 +00:00
|
|
|
]);
|
2018-07-29 16:18:24 +00:00
|
|
|
let list_ser = ron::ser::to_string(&list).unwrap();
|
|
|
|
|
|
|
|
let list_de: List = ron::de::from_str(list_ser.as_str()).unwrap();
|
|
|
|
let slice_de = list_de.as_slice();
|
|
|
|
let slice = list.as_slice();
|
|
|
|
assert_eq!(slice_de.len(), slice.len());
|
|
|
|
|
2019-08-11 07:33:34 +00:00
|
|
|
let fraction_de = slice_de[0].get::<Fraction>().expect("slice_de[0]").unwrap();
|
|
|
|
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
|
2018-07-29 16:18:24 +00:00
|
|
|
assert_eq!(fraction_de.0.numer(), fraction.0.numer());
|
|
|
|
assert_eq!(fraction_de.0.denom(), fraction.0.denom());
|
|
|
|
|
|
|
|
assert_eq!(
|
2019-08-11 07:33:34 +00:00
|
|
|
slice_de[1].get::<String>().expect("slice_de[1]").unwrap(),
|
|
|
|
slice[1].get::<String>().expect("slice[1]").unwrap()
|
2018-07-29 16:18:24 +00:00
|
|
|
);
|
2019-08-16 22:55:09 +00:00
|
|
|
|
|
|
|
assert!(slice[2].get::<String>().expect("slice[2]").is_none());
|
2019-08-19 13:32:40 +00:00
|
|
|
|
2019-08-21 09:06:03 +00:00
|
|
|
assert_eq!(
|
|
|
|
slice_de[3].get::<DateTime>().expect("slice_de[3]").unwrap(),
|
|
|
|
slice[3].get::<DateTime>().expect("slice[3]").unwrap()
|
|
|
|
);
|
2019-08-19 13:32:40 +00:00
|
|
|
|
|
|
|
assert!(slice[4].get::<DateTime>().expect("slice[4]").is_none());
|
2018-07-29 16:18:24 +00:00
|
|
|
}
|
2018-07-16 19:46:10 +00:00
|
|
|
}
|