You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
obws/src/serde/audio_tracks.rs

184 lines
5.0 KiB
Rust

use std::fmt;
use serde::{
de::{self, Deserializer, Visitor},
ser::{SerializeMap, Serializer},
};
#[derive(Debug, thiserror::Error)]
enum Error<'a> {
#[error("track index `{0}` is out of range")]
OutOfRange(&'a str),
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[bool; 6], D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(AudioTracksVisitor)
}
struct AudioTracksVisitor;
impl<'de> Visitor<'de> for AudioTracksVisitor {
type Value = [bool; 6];
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("audio tracks as key-value pairs")
}
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut map = [false; 6];
while let Some((key, value)) = access.next_entry::<std::borrow::Cow<'_, str>, bool>()? {
let index = match key.as_ref() {
"1" | "2" | "3" | "4" | "5" | "6" => key.as_bytes()[0] - b'0' - 1,
_ => return Err(de::Error::custom(Error::OutOfRange(key.as_ref()))),
};
map[index as usize] = value;
}
Ok(map)
}
}
pub mod option {
use super::*;
pub fn serialize<S>(value: &[Option<bool>; 6], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(value.iter().copied().flatten().count()))?;
for (k, v) in ["1", "2", "3", "4", "5", "6"]
.into_iter()
.zip(value)
.filter_map(|(k, v)| v.map(|v| (k, v)))
{
map.serialize_entry(k, &v)?;
}
map.end()
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, Token};
#[derive(Debug, PartialEq, Serialize)]
struct SimpleTracksSer {
#[serde(with = "super::option")]
value: [Option<bool>; 6],
}
#[derive(Debug, PartialEq, Deserialize)]
struct SimpleTracksDe {
#[serde(with = "super")]
value: [bool; 6],
}
#[test]
fn roundtrip() {
assert_ser_tokens(
&SimpleTracksSer {
value: [Some(true), Some(true), None, None, Some(false), Some(true)],
},
&[
Token::Struct {
name: "SimpleTracksSer",
len: 1,
},
Token::Str("value"),
Token::Map { len: Some(4) },
Token::Str("1"),
Token::Bool(true),
Token::Str("2"),
Token::Bool(true),
Token::Str("5"),
Token::Bool(false),
Token::Str("6"),
Token::Bool(true),
Token::MapEnd,
Token::StructEnd,
],
);
assert_de_tokens(
&SimpleTracksDe {
value: [true, true, false, false, false, true],
},
&[
Token::Struct {
name: "SimpleTracksDe",
len: 1,
},
Token::Str("value"),
Token::Map { len: Some(6) },
Token::Str("1"),
Token::Bool(true),
Token::String("2"),
Token::Bool(true),
Token::BorrowedStr("3"),
Token::Bool(false),
Token::Str("4"),
Token::Bool(false),
Token::Str("5"),
Token::Bool(false),
Token::Str("6"),
Token::Bool(true),
Token::MapEnd,
Token::StructEnd,
],
);
}
#[test]
fn deser() {
assert_de_tokens(
&SimpleTracksDe { value: [true; 6] },
&[
Token::Struct {
name: "SimpleTracksDe",
len: 1,
},
Token::Str("value"),
Token::Map { len: Some(6) },
Token::Str("1"),
Token::Bool(true),
Token::Str("2"),
Token::Bool(true),
Token::Str("3"),
Token::Bool(true),
Token::Str("4"),
Token::Bool(true),
Token::Str("5"),
Token::Bool(true),
Token::Str("6"),
Token::Bool(true),
Token::MapEnd,
Token::StructEnd,
],
);
assert_de_tokens_error::<SimpleTracksDe>(
&[
Token::Struct {
name: "SimpleTracksDe",
len: 1,
},
Token::Str("value"),
Token::Map { len: Some(1) },
Token::Str("10"),
Token::Bool(true),
Token::MapEnd,
],
"track index `10` is out of range",
);
}
}