Add unit tests for all custom (de)serializers
This commit is contained in:
parent
be2fca59b6
commit
5077adac0b
|
@ -34,6 +34,7 @@ tungstenite = { version = "0.11.1", default-features = false }
|
|||
anyhow = "1.0.37"
|
||||
dotenv = "0.15.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
serde_test = "1.0.118"
|
||||
tokio = { version = "0.3.6", features = ["fs", "macros", "rt-multi-thread", "time"] }
|
||||
|
||||
[features]
|
||||
|
|
392
src/de.rs
392
src/de.rs
|
@ -28,16 +28,16 @@ enum Error {
|
|||
ConversionFailed(String),
|
||||
}
|
||||
|
||||
pub fn duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
|
||||
pub fn duration_opt<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_option(OptDurationVisitor)
|
||||
deserializer.deserialize_option(DurationOptVisitor)
|
||||
}
|
||||
|
||||
struct OptDurationVisitor;
|
||||
struct DurationOptVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for OptDurationVisitor {
|
||||
impl<'de> Visitor<'de> for DurationOptVisitor {
|
||||
type Value = Option<Duration>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -86,12 +86,12 @@ pub fn duration_millis_opt<'de, D>(deserializer: D) -> Result<Option<Duration>,
|
|||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_i64(OptDurationMillisVisitor)
|
||||
deserializer.deserialize_option(DurationMillisOptVisitor)
|
||||
}
|
||||
|
||||
struct OptDurationMillisVisitor;
|
||||
struct DurationMillisOptVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for OptDurationMillisVisitor {
|
||||
impl<'de> Visitor<'de> for DurationMillisOptVisitor {
|
||||
type Value = Option<Duration>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -254,28 +254,382 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Context;
|
||||
use bitflags::bitflags;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn deser_duration() {
|
||||
fn deser_duration_opt() {
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(deserialize_with = "duration")]
|
||||
#[serde(deserialize_with = "duration_opt")]
|
||||
value: Option<Duration>,
|
||||
};
|
||||
|
||||
let input = json! {{ "value": "02:15:04.310" }};
|
||||
let expect = SimpleDuration {
|
||||
value: Some(
|
||||
Duration::hours(2)
|
||||
+ Duration::minutes(15)
|
||||
+ Duration::seconds(4)
|
||||
+ Duration::milliseconds(310),
|
||||
),
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(
|
||||
Duration::hours(2)
|
||||
+ Duration::minutes(15)
|
||||
+ Duration::seconds(4)
|
||||
+ Duration::milliseconds(310),
|
||||
),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Str("02:15:04.310"),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(
|
||||
Duration::hours(2)
|
||||
+ Duration::minutes(15)
|
||||
+ Duration::seconds(4)
|
||||
+ Duration::milliseconds(310),
|
||||
),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::Str("02:15:04.310"),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deser_duration_millis_opt() {
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(deserialize_with = "duration_millis_opt")]
|
||||
value: Option<Duration>,
|
||||
};
|
||||
assert_eq!(expect, serde_json::from_value(input).unwrap());
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(Duration::milliseconds(150)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(-1),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(Duration::milliseconds(150)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleDuration>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(u64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"value 18446744073709551615 is too large for an i64: \
|
||||
out of range integral type conversion attempted",
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(Duration::milliseconds(150)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deser_duration_millis() {
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(deserialize_with = "duration_millis")]
|
||||
value: Duration,
|
||||
};
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::milliseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::milliseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleDuration>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(u64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"value 18446744073709551615 is too large for an i64: \
|
||||
out of range integral type conversion attempted",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deser_duration_nanos() {
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(deserialize_with = "duration_nanos")]
|
||||
value: Duration,
|
||||
};
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::nanoseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::nanoseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleDuration>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(u64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"value 18446744073709551615 is too large for an i64: \
|
||||
out of range integral type conversion attempted",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deser_bitflags_u8() {
|
||||
bitflags! {
|
||||
struct Flags: u8 {
|
||||
const ONE = 1;
|
||||
const TWO = 2;
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Flags {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
Self::from_bits(value).context("unknown flags found")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleFlags {
|
||||
#[serde(deserialize_with = "bitflags_u8")]
|
||||
value: Flags,
|
||||
};
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleFlags {
|
||||
value: Flags::ONE | Flags::TWO,
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(3),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleFlags>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(i64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"value doesn't fit into an u8 integer: out of range integral type conversion attempted",
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleFlags {
|
||||
value: Flags::ONE | Flags::TWO,
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U8(3),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleFlags>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U8(100),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"conversion from u8 failed: unknown flags found",
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleFlags {
|
||||
value: Flags::ONE | Flags::TWO,
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(3),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleFlags>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(u64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"value doesn't fit into an u8 integer: out of range integral type conversion attempted",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ use crate::common::{SceneItem, SceneItemTransform};
|
|||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Event {
|
||||
#[serde(default, deserialize_with = "crate::de::duration")]
|
||||
#[serde(default, deserialize_with = "crate::de::duration_opt")]
|
||||
/// Time elapsed between now and stream start (only present if OBS Studio is streaming).
|
||||
pub stream_timecode: Option<Duration>,
|
||||
/// Time elapsed between now and recording start (only present if OBS Studio is recording).
|
||||
#[serde(default, deserialize_with = "crate::de::duration")]
|
||||
#[serde(default, deserialize_with = "crate::de::duration_opt")]
|
||||
pub rec_timecode: Option<Duration>,
|
||||
/// The type of event.
|
||||
#[serde(flatten)]
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use chrono::Duration;
|
||||
use rgb::RGBA8;
|
||||
use serde::ser::{Error, Serializer};
|
||||
use serde::ser::{self, Serializer};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("duration of {} days is too big to be serialized as nanoseconds", .0.num_days())]
|
||||
DurationTooBig(Duration),
|
||||
}
|
||||
|
||||
pub fn duration_millis_opt<S>(value: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -25,9 +31,7 @@ where
|
|||
{
|
||||
match value.num_nanoseconds() {
|
||||
Some(nanos) => serializer.serialize_i64(nanos),
|
||||
None => Err(Error::custom(
|
||||
"duration is too big to be serialized as nanoseconds",
|
||||
)),
|
||||
None => Err(ser::Error::custom(Error::DurationTooBig(*value))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,3 +61,201 @@ where
|
|||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bitflags::bitflags;
|
||||
use serde::Serialize;
|
||||
use serde_test::{assert_ser_tokens, assert_ser_tokens_error, Token};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ser_duration_millis_opt() {
|
||||
#[derive(Serialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(serialize_with = "duration_millis_opt")]
|
||||
value: Option<Duration>,
|
||||
}
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(Duration::milliseconds(150)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_duration_millis() {
|
||||
#[derive(Serialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(serialize_with = "duration_millis")]
|
||||
value: Duration,
|
||||
}
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::milliseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_duration_nanos() {
|
||||
#[derive(Serialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(serialize_with = "duration_nanos")]
|
||||
value: Duration,
|
||||
}
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration {
|
||||
value: Duration::nanoseconds(150),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(150),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens_error(
|
||||
&SimpleDuration {
|
||||
value: Duration::days(365_000_000),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
],
|
||||
"duration of 365000000 days is too big to be serialized as nanoseconds",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_bitflags_u8_opt() {
|
||||
bitflags! {
|
||||
struct Flags: u8 {
|
||||
const ONE = 1;
|
||||
const TWO = 2;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Flags> for u8 {
|
||||
fn from(value: Flags) -> Self {
|
||||
value.bits
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SimpleFlags {
|
||||
#[serde(serialize_with = "bitflags_u8_opt")]
|
||||
value: Option<Flags>,
|
||||
}
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleFlags {
|
||||
value: Some(Flags::ONE | Flags::TWO),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::U8(3),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleFlags { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleFlags",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_rgba8_inverse_opt() {
|
||||
#[derive(Serialize)]
|
||||
struct SimpleDuration {
|
||||
#[serde(serialize_with = "rgba8_inverse_opt")]
|
||||
value: Option<RGBA8>,
|
||||
}
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration {
|
||||
value: Some(RGBA8::new(1, 2, 3, 4)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::U32(0x04030201),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SimpleDuration { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleDuration",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,11 +44,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rgba8_inverse<'de, D>(deserializer: D) -> Result<Option<RGBA8>, D::Error>
|
||||
pub fn rgba8_inverse_opt<'de, D>(deserializer: D) -> Result<Option<RGBA8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_u32(Rgba8InverseOptVisitor)
|
||||
deserializer.deserialize_option(Rgba8InverseOptVisitor)
|
||||
}
|
||||
|
||||
struct Rgba8InverseOptVisitor;
|
||||
|
@ -60,6 +60,16 @@ impl<'de> Visitor<'de> for Rgba8InverseOptVisitor {
|
|||
formatter.write_str("a RGBA color value encoded as integer in inverse order (ABGR)")
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match u32::try_from(v) {
|
||||
Ok(v) => self.visit_u32(v),
|
||||
Err(e) => Err(Error::custom(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
|
@ -72,16 +82,6 @@ impl<'de> Visitor<'de> for Rgba8InverseOptVisitor {
|
|||
)))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match u32::try_from(v) {
|
||||
Ok(v) => self.visit_u32(v),
|
||||
Err(e) => Err(Error::custom(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
|
@ -110,7 +110,7 @@ impl<'de> Visitor<'de> for Rgba8InverseOptVisitor {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -122,10 +122,128 @@ mod tests {
|
|||
value: Vec<String>,
|
||||
}
|
||||
|
||||
let input = json! {{ "value": "a,b,c" }};
|
||||
let expect = SimpleList {
|
||||
value: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()],
|
||||
};
|
||||
assert_eq!(expect, serde_json::from_value(input).unwrap());
|
||||
assert_de_tokens(
|
||||
&SimpleList {
|
||||
value: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()],
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleList",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Str("a,b,c"),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deser_rgba8_inverse_opt() {
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
struct SimpleColor {
|
||||
#[serde(deserialize_with = "rgba8_inverse_opt")]
|
||||
value: Option<RGBA8>,
|
||||
}
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleColor {
|
||||
value: Some(RGBA8::new(1, 2, 3, 4)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(0x04030201),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleColor>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::I64(i64::MIN),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"out of range integral type conversion attempted",
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleColor {
|
||||
value: Some(RGBA8::new(1, 2, 3, 4)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U32(0x04030201),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleColor {
|
||||
value: Some(RGBA8::new(1, 2, 3, 4)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(0x04030201),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens_error::<SimpleColor>(
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::U64(u64::MAX),
|
||||
Token::StructEnd,
|
||||
],
|
||||
"out of range integral type conversion attempted",
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleColor { value: None },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::None,
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&SimpleColor {
|
||||
value: Some(RGBA8::new(1, 2, 3, 4)),
|
||||
},
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "SimpleColor",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("value"),
|
||||
Token::Some,
|
||||
Token::U32(0x04030201),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,10 +295,10 @@ pub struct TextFreetype2Properties {
|
|||
/// Source name.
|
||||
pub source: String,
|
||||
/// Gradient top color.
|
||||
#[serde(default, deserialize_with = "de::rgba8_inverse")]
|
||||
#[serde(default, deserialize_with = "de::rgba8_inverse_opt")]
|
||||
pub color1: Option<RGBA8>,
|
||||
/// Gradient bottom color.
|
||||
#[serde(default, deserialize_with = "de::rgba8_inverse")]
|
||||
#[serde(default, deserialize_with = "de::rgba8_inverse_opt")]
|
||||
pub color2: Option<RGBA8>,
|
||||
/// Custom width (0 to disable).
|
||||
pub custom_width: Option<u32>,
|
||||
|
@ -540,10 +540,10 @@ pub struct StreamingStatus {
|
|||
/// Current recording status.
|
||||
pub recording: bool,
|
||||
/// Time elapsed since streaming started (only present if currently streaming).
|
||||
#[serde(deserialize_with = "crate::de::duration")]
|
||||
#[serde(deserialize_with = "crate::de::duration_opt")]
|
||||
pub stream_timecode: Option<Duration>,
|
||||
/// Time elapsed since recording started (only present if currently recording).
|
||||
#[serde(deserialize_with = "crate::de::duration")]
|
||||
#[serde(deserialize_with = "crate::de::duration_opt")]
|
||||
pub rec_timecode: Option<Duration>,
|
||||
/// Always false. Retrocompatibility with OBSRemote.
|
||||
#[serde(default)]
|
||||
|
|
Loading…
Reference in a new issue