Fix serde for Values with optional Types

Attention: these changes induce breaking changes for the serde
representation of some types.

Serialization of `Value`s used to rely on the `get` function
followed by an `unwrap`. This means that optional types couldn't
be serialized when they were `None`.

This commit distinguishes between the optional `Value` `Types` and
the ones that always return some value.

The `Value`s, `Structure` fields and `Tag`s with following types are
now represented as `Option`s:

- `Buffer`
- `DateTime`
- `Sample` (note: this type is used for `Tag` images)
- `String` (except for `Tag`s - see the comment in `tags_serde.rs`).

The representations for these `Type`s remain unchanged when they are
used in-place (not as part of a `Value`).

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/215
This commit is contained in:
François Laignel 2019-08-17 00:55:09 +02:00
parent 26423a069c
commit c74eef374a
4 changed files with 202 additions and 126 deletions

View file

@ -254,7 +254,7 @@ mod tests {
" ((\"foo/bar\", [",
" (\"int\", \"i32\", 12),",
" (\"bool\", \"bool\", true),",
" (\"string\", \"String\", \"bla\"),",
" (\"string\", \"String\", Some(\"bla\")),",
" (\"fraction\", \"Fraction\", (1, 2)),",
" (\"array\", \"Array\", [",
" (\"i32\", 1),",
@ -286,7 +286,7 @@ mod tests {
" ((\"foo/bar\", [",
" (\"int\", \"i32\", 12),",
" (\"bool\", \"bool\", true),",
" (\"string\", \"String\", \"bla\"),",
" (\"string\", \"String\", Some(\"bla\")),",
" (\"fraction\", \"Fraction\", (1, 2)),",
" (\"array\", \"Array\", [",
" (\"i32\", 1),",
@ -321,7 +321,7 @@ mod tests {
" ((\"foo/bar\", [",
" (\"int\", \"i32\", 12),",
" (\"bool\", \"bool\", true),",
" (\"string\", \"String\", \"bla\"),",
" (\"string\", \"String\", Some(\"bla\")),",
" (\"fraction\", \"Fraction\", (1, 2)),",
" (\"array\", \"Array\", [",
" (\"i32\", 1),",
@ -363,7 +363,7 @@ mod tests {
("foo/bar", [
("int", "i32", 12),
("bool", "bool", true),
("string", "String", "bla"),
("string", "String", Some("bla")),
("fraction", "Fraction", (1, 2)),
("array", "Array", [
("i32", 1),
@ -396,7 +396,7 @@ mod tests {
("foo/bar", [
("int", "i32", 12),
("bool", "bool", true),
("string", "String", "bla"),
("string", "String", None),
("fraction", "Fraction", (1, 2)),
("array", "Array", [
("i32", 1),
@ -408,6 +408,7 @@ mod tests {
])"#;
let caps: Caps = ron::de::from_str(caps_ron).unwrap();
let s = caps.get_structure(0).unwrap();
let str_none: Option<&str> = None;
assert_eq!(
s,
Structure::new(
@ -415,7 +416,7 @@ mod tests {
&[
("int", &12),
("bool", &true),
("string", &"bla"),
("string", &str_none),
("fraction", &Fraction::new(1, 2)),
("array", &Array::new(&[&1, &2])),
],
@ -431,7 +432,7 @@ mod tests {
("foo/bar", [
("int", "i32", 12),
("bool", "bool", true),
("string", "String", "bla"),
("string", "String", Some("bla")),
("fraction", "Fraction", (1, 2)),
("array", "Array", [
("i32", 1),

View file

@ -189,8 +189,8 @@ mod tests {
assert_eq!(
Ok(concat!(
"(\"test\", [",
" (\"f1\", \"String\", \"abc\"),",
" (\"f2\", \"String\", \"bcd\"),",
" (\"f1\", \"String\", Some(\"abc\")),",
" (\"f2\", \"String\", Some(\"bcd\")),",
" (\"f3\", \"i32\", 123),",
" (\"fraction\", \"Fraction\", (1, 2)),",
" (\"array\", \"Array\", [",
@ -210,8 +210,8 @@ mod tests {
let s_ron = r#"
("test", [
("f1", "String", "abc"),
("f2", "String", "bcd"),
("f1", "String", Some("abc")),
("f2", "String", Some("bcd")),
("f3", "i32", 123),
("fraction", "Fraction", (1, 2)),
("array", "Array", [
@ -242,7 +242,7 @@ mod tests {
let s = Structure::builder("test")
.field("f1", &"abc")
.field("f2", &String::from("bcd"))
.field("f2", &"bcd".to_owned())
.field("f3", &123i32)
.field("fraction", &Fraction::new(1, 2))
.field("array", &Array::new(&[&1, &2]))

View file

@ -27,13 +27,26 @@ use Sample;
use TagMergeMode;
use TagScope;
macro_rules! ser_tag (
macro_rules! ser_some_tag (
($value:ident, $seq:ident, $t:ty) => (
ser_value!($value, $t, |_, value| {
ser_some_value!($value, $t, |_, value| {
$seq.serialize_element(&value)
})
);
);
macro_rules! ser_opt_tag (
($value:ident, $seq:ident, $t:ty) => (
ser_opt_value!($value, $t, |_, value| {
$seq.serialize_element(&value)
})
);
);
// Note: unlike `Value`s, `Tag`s with optional `Type` `String` & `Date` values are guarenteed
// to be Non-null and non-empty in the C API. See:
// https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/d90d771a9a512381315f7694c3a50b152035f3cb/gst/gststructure.c#L810-853
// FIXME: implement serde for type `Date`
// serialize trait is only available for `&self`, but we need to mutate the iterator
struct TagValuesSer<'a>(Rc<RefCell<GenericTagIter<'a>>>);
@ -51,15 +64,20 @@ impl<'a> Serialize for TagValuesSer<'a> {
let mut seq = serializer.serialize_seq(tag_iter.size_hint().1)?;
for value in tag_iter.deref_mut() {
match value.type_() {
glib::Type::F64 => ser_tag!(value, seq, f64),
glib::Type::String => ser_tag!(value, seq, String),
glib::Type::U32 => ser_tag!(value, seq, u32),
glib::Type::U64 => ser_tag!(value, seq, u64),
glib::Type::F64 => ser_some_tag!(value, seq, f64),
glib::Type::String => {
// See above comment about `Tag`s with `String` values
ser_opt_value!(value, String, |_, value: Option<String>| {
seq.serialize_element(&value.expect("string tag ser"))
})
}
glib::Type::U32 => ser_some_tag!(value, seq, u32),
glib::Type::U64 => ser_some_tag!(value, seq, u64),
glib::Type::Other(type_id) => {
if *DATE_TIME_OTHER_TYPE_ID == type_id {
ser_tag!(value, seq, DateTime)
ser_opt_tag!(value, seq, DateTime)
} else if *SAMPLE_OTHER_TYPE_ID == type_id {
ser_tag!(value, seq, Sample)
ser_opt_tag!(value, seq, Sample)
} else {
Err(ser::Error::custom(format!(
"unimplemented `Tag` serialization for type {}",
@ -128,9 +146,14 @@ impl Serialize for TagList {
}
}
macro_rules! de_tag_value(
macro_rules! de_some_tag(
($tag_name:expr, $seq:expr, $t:ty) => (
de_send_value!("Tag", $tag_name, $seq, $t)
de_some_send_value!("Tag", $tag_name, $seq, $t)
);
);
macro_rules! de_opt_tag(
($tag_name:expr, $seq:expr, $t:ty) => (
de_opt_send_value!("Tag", $tag_name, $seq, $t)
);
);
@ -152,15 +175,18 @@ impl<'de, 'a> Visitor<'de> for TagValuesVisitor<'a> {
loop {
let tag_value = match tag_type {
glib::Type::F64 => de_tag_value!(self.0, seq, f64),
glib::Type::String => de_tag_value!(self.0, seq, String),
glib::Type::U32 => de_tag_value!(self.0, seq, u32),
glib::Type::U64 => de_tag_value!(self.0, seq, u64),
glib::Type::F64 => de_some_tag!(self.0, seq, f64),
glib::Type::String => {
// See comment above `TagValuesSer` definition about `Tag`s with `String` values
de_some_tag!(self.0, seq, String)
}
glib::Type::U32 => de_some_tag!(self.0, seq, u32),
glib::Type::U64 => de_some_tag!(self.0, seq, u64),
glib::Type::Other(type_id) => {
if *DATE_TIME_OTHER_TYPE_ID == type_id {
de_tag_value!(self.0, seq, DateTime)
de_opt_tag!(self.0, seq, DateTime)
} else if *SAMPLE_OTHER_TYPE_ID == type_id {
de_tag_value!(self.0, seq, Sample)
de_opt_tag!(self.0, seq, Sample)
} else {
return Err(de::Error::custom(format!(
"unimplemented deserialization for `Tag` {} with type `{}`",
@ -339,10 +365,10 @@ mod tests {
" 1,",
" ]),",
" (\"datetime\", [",
" YMD(2018, 5, 28),",
" Some(YMD(2018, 5, 28)),",
" ]),",
" (\"image\", [",
" (",
" Some((",
" buffer: Some((",
" pts: None,",
" dts: None,",
@ -372,7 +398,7 @@ mod tests {
" duration: -1,",
" )),",
" info: None,",
" ),",
" )),",
" ]),",
" ],",
")",
@ -400,10 +426,10 @@ mod tests {
("bitrate", [96000]),
("replaygain-track-gain", [1]),
("datetime", [
YMD(2018, 5, 28),
Some(YMD(2018, 5, 28)),
]),
("image", [
(
Some((
buffer: Some((
pts: None,
dts: None,
@ -419,7 +445,7 @@ mod tests {
caps: None,
segment: None,
info: None,
),
)),
])
],
)
@ -433,11 +459,11 @@ mod tests {
Some("another title")
);
assert_eq!(
tags.get_index::<Duration>(0).unwrap().get(),
Some(::SECOND * 120)
tags.get_index::<Duration>(0).unwrap().get_some(),
::SECOND * 120
);
assert_eq!(tags.get_index::<Bitrate>(0).unwrap().get(), Some(96_000));
assert_eq!(tags.get_index::<TrackGain>(0).unwrap().get(), Some(1f64));
assert_eq!(tags.get_index::<Bitrate>(0).unwrap().get_some(), 96_000);
assert_eq!(tags.get_index::<TrackGain>(0).unwrap().get_some(), 1f64);
let datetime = tags.get_index::<DateTime>(0).unwrap().get().unwrap();
assert_eq!(datetime.get_year(), 2018);
assert_eq!(datetime.get_month(), 5);
@ -470,8 +496,8 @@ mod tests {
tags.get_index::<Title>(1).unwrap().get(),
Some("another title")
);
assert_eq!(tags.get_index::<Bitrate>(0).unwrap().get(), Some(96_000));
assert_eq!(tags.get_index::<TrackGain>(0).unwrap().get(), Some(1f64));
assert_eq!(tags.get_index::<Bitrate>(0).unwrap().get_some(), 96_000);
assert_eq!(tags.get_index::<TrackGain>(0).unwrap().get_some(), 1f64);
let datetime = tags.get_index::<DateTime>(0).unwrap().get().unwrap();
assert_eq!(datetime.get_year(), 2018);
assert_eq!(datetime.get_month(), 5);
@ -525,16 +551,16 @@ mod tests {
tags.get_index::<Title>(1).unwrap().get(),
);
assert_eq!(
tags_de.get_index::<Duration>(0).unwrap().get(),
tags.get_index::<Duration>(0).unwrap().get(),
tags_de.get_index::<Duration>(0).unwrap().get_some(),
tags.get_index::<Duration>(0).unwrap().get_some(),
);
assert_eq!(
tags_de.get_index::<Bitrate>(0).unwrap().get(),
tags.get_index::<Bitrate>(0).unwrap().get(),
tags_de.get_index::<Bitrate>(0).unwrap().get_some(),
tags.get_index::<Bitrate>(0).unwrap().get_some(),
);
assert_eq!(
tags_de.get_index::<TrackGain>(0).unwrap().get(),
tags.get_index::<TrackGain>(0).unwrap().get(),
tags_de.get_index::<TrackGain>(0).unwrap().get_some(),
tags.get_index::<TrackGain>(0).unwrap().get_some(),
);
let datetime = tags.get_index::<DateTime>(0).unwrap().get().unwrap();
assert_eq!(datetime.get_year(), 2018);

View file

@ -31,6 +31,8 @@ fn get_other_type_id<T: StaticType>() -> usize {
}
}
// FIXME: implement serde for type `Date`
lazy_static! {
pub(crate) static ref ARRAY_OTHER_TYPE_ID: usize = get_other_type_id::<Array>();
pub(crate) static ref BITMASK_OTHER_TYPE_ID: usize = get_other_type_id::<Bitmask>();
@ -58,48 +60,56 @@ impl<'de> Deserialize<'de> for Fraction {
}
}
macro_rules! ser_value (
macro_rules! ser_some_value (
($value:expr, $t:ty, $ser_closure:expr) => (
{
// FIXME: This should serialize to an `Option` when the `Type` allows it
// See https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/215
let value = $value.get::<$t>().expect("Value serialization macro").unwrap();
let value = $value.get_some::<$t>().expect("ser_some_value macro");
$ser_closure(stringify!($t), value)
}
);
);
macro_rules! ser_opt_value (
($value:expr, $t:ty, $ser_closure:expr) => (
{
let value = $value.get::<$t>().expect("ser_opt_value macro");
$ser_closure(stringify!($t), value)
}
);
);
macro_rules! ser_value (
($value:expr, $ser_closure:expr) => (
match $value.type_() {
glib::Type::I8 => ser_value!($value, i8, $ser_closure),
glib::Type::U8 => ser_value!($value, u8, $ser_closure),
glib::Type::Bool => ser_value!($value, bool, $ser_closure),
glib::Type::I32 => ser_value!($value, i32, $ser_closure),
glib::Type::U32 => ser_value!($value, u32, $ser_closure),
glib::Type::I64 => ser_value!($value, i64, $ser_closure),
glib::Type::U64 => ser_value!($value, u64, $ser_closure),
glib::Type::F32 => ser_value!($value, f32, $ser_closure),
glib::Type::F64 => ser_value!($value, f64, $ser_closure),
glib::Type::String => ser_value!($value, String, $ser_closure),
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),
glib::Type::Other(type_id) => {
if *ARRAY_OTHER_TYPE_ID == type_id {
ser_value!($value, Array, $ser_closure)
ser_some_value!($value, Array, $ser_closure)
} else if *BITMASK_OTHER_TYPE_ID == type_id {
ser_value!($value, Bitmask, $ser_closure)
ser_some_value!($value, Bitmask, $ser_closure)
} else if *DATE_TIME_OTHER_TYPE_ID == type_id {
ser_value!($value, DateTime, $ser_closure)
ser_opt_value!($value, DateTime, $ser_closure)
} else if *FRACTION_OTHER_TYPE_ID == type_id {
ser_value!($value, Fraction, $ser_closure)
ser_some_value!($value, Fraction, $ser_closure)
} else if *FRACTION_RANGE_OTHER_TYPE_ID == type_id {
ser_value!($value, FractionRange, $ser_closure)
ser_some_value!($value, FractionRange, $ser_closure)
} else if *INT_RANGE_I32_OTHER_TYPE_ID == type_id {
ser_value!($value, IntRange<i32>, $ser_closure)
ser_some_value!($value, IntRange<i32>, $ser_closure)
} else if *INT_RANGE_I64_OTHER_TYPE_ID == type_id {
ser_value!($value, IntRange<i64>, $ser_closure)
ser_some_value!($value, IntRange<i64>, $ser_closure)
} else if *LIST_OTHER_TYPE_ID == type_id {
ser_value!($value, List, $ser_closure)
ser_some_value!($value, List, $ser_closure)
} else if *SAMPLE_OTHER_TYPE_ID == type_id {
ser_value!($value, Sample, $ser_closure)
ser_opt_value!($value, Sample, $ser_closure)
} else if *BUFFER_OTHER_TYPE_ID == type_id {
ser_value!($value, Buffer, $ser_closure)
ser_opt_value!($value, Buffer, $ser_closure)
} else {
Err(
ser::Error::custom(
@ -162,25 +172,28 @@ macro_rules! impl_ser_send_value_collection (
impl_ser_send_value_collection!(Array);
impl_ser_send_value_collection!(List);
macro_rules! de_value(
($seq:expr, $t:ty) => (
{
let value = $seq
.next_element::<$t>()?
.map(|base_value| base_value.to_value());
Ok(value)
}
);
);
macro_rules! de_send_value(
macro_rules! de_some_send_value(
($type_name:expr, $seq:expr, $t:ty) => (
de_send_value!("Value", $type_name, $seq, $t)
de_some_send_value!("Value", $type_name, $seq, $t)
);
($outer_type:expr, $type_name:expr, $seq:expr, $t:ty) => (
match de_value!($seq, $t)? {
Some(value) => {
let glib_send_value = value
de_send_value!($outer_type, $type_name, $seq, $t, $t)
);
);
macro_rules! de_opt_send_value(
($type_name:expr, $seq:expr, $t:ty) => (
de_opt_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, 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()
.try_into_send_value::<$t>()
.map_err(|_|
de::Error::custom(format!(
@ -188,33 +201,33 @@ macro_rules! de_send_value(
$outer_type,
$type_name,
))
)?;
Ok(Some(SendValue::from(glib_send_value)))
}
None => Ok(None)
)?
))
}
None => None
})
);
($type_name:expr, $seq:expr) => (
match $type_name.as_str() {
"i8" => de_send_value!($type_name, $seq, i8),
"u8" => de_send_value!($type_name, $seq, u8),
"bool" => de_send_value!($type_name, $seq, bool),
"i32" => de_send_value!($type_name, $seq, i32),
"u32" => de_send_value!($type_name, $seq, u32),
"i64" => de_send_value!($type_name, $seq, i64),
"u64" => de_send_value!($type_name, $seq, u64),
"f32" => de_send_value!($type_name, $seq, f32),
"f64" => de_send_value!($type_name, $seq, f64),
"String" => de_send_value!($type_name, $seq, String),
"Array" => de_send_value!($type_name, $seq, Array),
"Bitmask" => de_send_value!($type_name, $seq, Bitmask),
"DateTime" => de_send_value!($type_name, $seq, DateTime),
"Fraction" => de_send_value!($type_name, $seq, Fraction),
"FractionRange" => de_send_value!($type_name, $seq, FractionRange),
"IntRange<i32>" => de_send_value!($type_name, $seq, IntRange<i32>),
"IntRange<i64>" => de_send_value!($type_name, $seq, IntRange<i64>),
"Sample" => de_send_value!($type_name, $seq, Sample),
"Buffer" => de_send_value!($type_name, $seq, Buffer),
"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),
"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),
_ => return Err(
de::Error::custom(
format!(
@ -344,7 +357,16 @@ mod tests {
let value_str = "test str".to_value();
let send_value_str = value_str.try_into_send_value::<String>().unwrap();
let array = Array::new(&[&send_value_13, &send_value_12, &send_value_str]);
let str_none: Option<&str> = None;
let value_str_none = str_none.to_value();
let send_value_str_none = value_str_none.try_into_send_value::<String>().unwrap();
let array = Array::new(&[
&send_value_13,
&send_value_12,
&send_value_str,
&send_value_str_none,
]);
let res = ron::ser::to_string_pretty(&array, pretty_config.clone());
assert_eq!(
@ -352,7 +374,8 @@ mod tests {
"[",
" (\"Fraction\", (1, 3)),",
" (\"Fraction\", (1, 2)),",
" (\"String\", \"test str\"),",
" (\"String\", Some(\"test str\")),",
" (\"String\", None),",
"]"
)
.to_owned()),
@ -361,7 +384,8 @@ mod tests {
let res = serde_json::to_string(&array).unwrap();
assert_eq!(
"[[\"Fraction\",[1,3]],[\"Fraction\",[1,2]],[\"String\",\"test str\"]]".to_owned(),
"[[\"Fraction\",[1,3]],[\"Fraction\",[1,2]],[\"String\",\"test str\"],[\"String\",null]]"
.to_owned(),
res
);
@ -372,14 +396,19 @@ mod tests {
let value_str = "test str".to_value();
let send_value_str = value_str.try_into_send_value::<String>().unwrap();
let list = List::new(&[&send_value_12, &send_value_str]);
let str_none: Option<&str> = None;
let value_str_none = str_none.to_value();
let send_value_str_none = value_str_none.try_into_send_value::<String>().unwrap();
let list = List::new(&[&send_value_12, &send_value_str, &send_value_str_none]);
let res = ron::ser::to_string_pretty(&list, pretty_config.clone());
assert_eq!(
Ok(concat!(
"[",
" (\"Fraction\", (1, 2)),",
" (\"String\", \"test str\"),",
" (\"String\", Some(\"test str\")),",
" (\"String\", None),",
"]"
)
.to_owned()),
@ -387,7 +416,6 @@ mod tests {
);
}
#[cfg(feature = "ser_de")]
#[test]
fn test_deserialize_simple() {
::init().unwrap();
@ -439,7 +467,6 @@ mod tests {
assert_eq!(bitmask_ref, bitmask);
}
#[cfg(feature = "ser_de")]
#[test]
fn test_serde_roundtrip_simple() {
::init().unwrap();
@ -480,7 +507,6 @@ mod tests {
assert_eq!(bitmask_de, bitmask);
}
#[cfg(feature = "ser_de")]
#[test]
fn test_deserialize_collections() {
::init().unwrap();
@ -489,11 +515,12 @@ mod tests {
let array_ron = r#"[
("Fraction", (1, 3)),
("Fraction", (1, 2)),
("String", "test str"),
("String", Some("test str")),
("String", None),
]"#;
let array: Array = ron::de::from_str(array_ron).unwrap();
let slice = array.as_slice();
assert_eq!(3, slice.len());
assert_eq!(4, slice.len());
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
assert_eq!(fraction.0.numer(), &1);
@ -508,10 +535,13 @@ mod tests {
slice[2].get::<String>().expect("slice[2]").unwrap()
);
let array_json = r#"[["Fraction",[1,3]],["Fraction",[1,2]],["String","test str"]]"#;
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
let array_json =
r#"[["Fraction",[1,3]],["Fraction",[1,2]],["String","test str"],["String",null]]"#;
let array: Array = serde_json::from_str(array_json).unwrap();
let slice = array.as_slice();
assert_eq!(3, slice.len());
assert_eq!(4, slice.len());
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
assert_eq!(fraction.0.numer(), &1);
@ -526,14 +556,17 @@ mod tests {
slice[2].get::<String>().expect("slice[2]").unwrap()
);
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
// List
let list_ron = r#"[
("Fraction", (1, 2)),
("String", "test str"),
("String", Some("test str")),
("String", None),
]"#;
let list: List = ron::de::from_str(list_ron).unwrap();
let slice = list.as_slice();
assert_eq!(2, slice.len());
assert_eq!(3, slice.len());
let fraction = slice[0].get::<Fraction>().expect("slice[0]").unwrap();
assert_eq!(fraction.0.numer(), &1);
@ -543,9 +576,10 @@ mod tests {
"test str".to_owned(),
slice[1].get::<String>().expect("slice[1]").unwrap()
);
assert!(slice[2].get::<String>().expect("slice[2]").is_none());
}
#[cfg(feature = "ser_de")]
#[test]
fn test_serde_roundtrip_collection() {
use glib::value::ToValue;
@ -559,7 +593,15 @@ mod tests {
let send_value_12 = value_12.try_into_send_value::<Fraction>().unwrap();
let value_str = "test str".to_value();
let send_value_str = value_str.try_into_send_value::<String>().unwrap();
let array = Array::new(&[&send_value_13, &send_value_12, &send_value_str]);
let str_none: Option<&str> = None;
let value_str_none = str_none.to_value();
let send_value_str_none = value_str_none.try_into_send_value::<String>().unwrap();
let array = Array::new(&[
&send_value_13,
&send_value_12,
&send_value_str,
&send_value_str_none,
]);
let array_ser = ron::ser::to_string(&array).unwrap();
let array_de: Array = ron::de::from_str(array_ser.as_str()).unwrap();
@ -582,12 +624,17 @@ mod tests {
slice[2].get::<String>().expect("slice[2]").unwrap()
);
assert!(slice[3].get::<String>().expect("slice[3]").is_none());
// List
let value_12 = Fraction::new(1, 2).to_value();
let send_value_12 = value_12.try_into_send_value::<Fraction>().unwrap();
let value_str = "test str".to_value();
let send_value_str = value_str.try_into_send_value::<String>().unwrap();
let list = List::new(&[&send_value_12, &send_value_str]);
let str_none: Option<&str> = None;
let value_str_none = str_none.to_value();
let send_value_str_none = value_str_none.try_into_send_value::<String>().unwrap();
let list = List::new(&[&send_value_12, &send_value_str, &send_value_str_none]);
let list_ser = ron::ser::to_string(&list).unwrap();
let list_de: List = ron::de::from_str(list_ser.as_str()).unwrap();
@ -604,5 +651,7 @@ mod tests {
slice_de[1].get::<String>().expect("slice_de[1]").unwrap(),
slice[1].get::<String>().expect("slice[1]").unwrap()
);
assert!(slice[2].get::<String>().expect("slice[2]").is_none());
}
}