From 8adcdf55fdb0b925feba77fd441cfae875cc149d Mon Sep 17 00:00:00 2001 From: "Carson M." Date: Mon, 18 Mar 2024 18:05:35 -0500 Subject: [PATCH] feat: `serde` support, closes #2 --- Cargo.toml | 6 ++ src/audio.rs | 2 + src/break.rs | 2 + src/element.rs | 153 ++++++++++++++++++++++++++++++++++++++++++- src/emphasis.rs | 2 + src/lib.rs | 2 + src/mark.rs | 1 + src/mstts/express.rs | 40 +++++------ src/mstts/mod.rs | 8 +++ src/speak.rs | 1 + src/text.rs | 1 + src/unit.rs | 2 + src/visit.rs | 23 ++++++- src/visit_mut.rs | 23 ++++++- src/voice.rs | 3 + 15 files changed, 246 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 137c4ae..cea2a60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,9 @@ repository = "https://github.com/pykeio/ssml" [dependencies] dyn-clone = "1.0" +serde = { version = "1.0", optional = true, features = [ "derive" ] } +erased-serde = { version = "0.4", optional = true } + +[features] +default = [] +serde = [ "dep:serde", "dep:erased-serde" ] diff --git a/src/audio.rs b/src/audio.rs index a2842cc..c3399ff 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -5,6 +5,7 @@ use crate::{ /// Specify repeating an [`Audio`] element's playback for a certain number of times, or for a determined duration. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum AudioRepeat { /// Repeat the audio a certain number of times. A fractional value is allowed and describes a portion of the /// rendered media. @@ -18,6 +19,7 @@ pub enum AudioRepeat { /// [`Audio`] supports the insertion of recorded audio files and the insertion of other audio formats in conjunction /// with synthesized speech output. #[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Audio { src: String, desc: Option, diff --git a/src/break.rs b/src/break.rs index 27aee9e..dc68b05 100644 --- a/src/break.rs +++ b/src/break.rs @@ -1,6 +1,7 @@ use crate::{Serialize, SerializeOptions, TimeDesignation, XmlWriter}; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum BreakStrength { None, ExtraWeak, @@ -12,6 +13,7 @@ pub enum BreakStrength { } #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Break { Strength(BreakStrength), Time(TimeDesignation) diff --git a/src/element.rs b/src/element.rs index f483479..bc2c1a0 100644 --- a/src/element.rs +++ b/src/element.rs @@ -37,10 +37,12 @@ macro_rules! el { } }; } +pub(crate) use el; el! { /// Represents all SSML elements. #[derive(Clone, Debug)] + #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[non_exhaustive] pub enum Element { Text(Text), @@ -50,6 +52,7 @@ el! { Break(Break), Emphasis(Emphasis), Mark(Mark), + FlavorMSTTS(crate::mstts::Element), /// A dyn element can be used to implement your own custom elements outside of the `ssml` crate. See /// [`DynElement`] for more information and examples. Dyn(Box) @@ -64,6 +67,139 @@ el! { } } +#[cfg(feature = "serde")] +impl<'a> serde::Deserialize<'a> for Element { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'a> + { + #[allow(non_camel_case_types)] + enum ElementField { + Text, + Audio, + Voice, + Meta, + Break, + Emphasis, + Mark + } + + struct ElementFieldVisitor; + + impl<'de> serde::de::Visitor<'de> for ElementFieldVisitor { + type Value = ElementField; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("variant identifier") + } + + fn visit_u64<__E>(self, val: u64) -> serde::__private::Result + where + __E: serde::de::Error + { + match val { + 0u64 => Ok(ElementField::Text), + 1u64 => Ok(ElementField::Audio), + 2u64 => Ok(ElementField::Voice), + 3u64 => Ok(ElementField::Meta), + 4u64 => Ok(ElementField::Break), + 5u64 => Ok(ElementField::Emphasis), + 6u64 => Ok(ElementField::Mark), + 7u64 => Err(serde::de::Error::custom("DynElements cannot be deserialized")), + _ => Err(serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(val), &"variant index 0 <= i < 8")) + } + } + + fn visit_str(self, val: &str) -> Result + where + E: serde::de::Error + { + match val { + "Text" => Ok(ElementField::Text), + "Audio" => Ok(ElementField::Audio), + "Voice" => Ok(ElementField::Voice), + "Meta" => Ok(ElementField::Meta), + "Break" => Ok(ElementField::Break), + "Emphasis" => Ok(ElementField::Emphasis), + "Mark" => Ok(ElementField::Mark), + "Dyn" => Err(serde::de::Error::custom("DynElements cannot be deserialized")), + _ => Err(serde::de::Error::unknown_variant(val, VARIANTS)) + } + } + + fn visit_bytes(self, val: &[u8]) -> serde::__private::Result + where + E: serde::de::Error + { + match val { + b"Text" => Ok(ElementField::Text), + b"Audio" => Ok(ElementField::Audio), + b"Voice" => Ok(ElementField::Voice), + b"Meta" => Ok(ElementField::Meta), + b"Break" => Ok(ElementField::Break), + b"Emphasis" => Ok(ElementField::Emphasis), + b"Mark" => Ok(ElementField::Mark), + b"Dyn" => Err(serde::de::Error::custom("DynElements cannot be deserialized")), + _ => { + let __value = &String::from_utf8_lossy(val); + Err(serde::de::Error::unknown_variant(__value, VARIANTS)) + } + } + } + } + + impl<'de> serde::Deserialize<'de> for ElementField { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de> + { + serde::Deserializer::deserialize_identifier(deserializer, ElementFieldVisitor) + } + } + + #[doc(hidden)] + struct Visitor<'de> { + marker: std::marker::PhantomData, + lifetime: std::marker::PhantomData<&'de ()> + } + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = Element; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("enum Element") + } + + fn visit_enum(self, data: A) -> Result + where + A: serde::de::EnumAccess<'de> + { + match serde::de::EnumAccess::variant(data)? { + (ElementField::Text, variant) => serde::de::VariantAccess::newtype_variant::(variant).map(Element::Text), + (ElementField::Audio, variant) => serde::de::VariantAccess::newtype_variant::