Skip to content

Commit

Permalink
refactor!: remove checks
Browse files Browse the repository at this point in the history
A custom `Visit`or can fulfill this functionality if needed. It doesn't make a whole lot of sense to include it in the `ssml` crate.
  • Loading branch information
decahedron1 committed Nov 11, 2024
1 parent 1a38b32 commit 6cfd74a
Show file tree
Hide file tree
Showing 6 changed files with 11 additions and 113 deletions.
45 changes: 1 addition & 44 deletions src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloc::{borrow::Cow, string::ToString, vec::Vec};
use core::fmt::{self, Display, Write};

use crate::{
Element, Flavor, Serialize, SerializeOptions, XmlWriter,
Element, Serialize, SerializeOptions, XmlWriter,
unit::{Decibels, TimeDesignation},
util,
xml::TrustedNoEscape
Expand Down Expand Up @@ -173,23 +173,6 @@ impl<'s> Audio<'s> {

impl<'s> Serialize for Audio<'s> {
fn serialize_xml<W: Write>(&self, writer: &mut XmlWriter<W>, options: &SerializeOptions) -> crate::Result<()> {
if options.perform_checks {
if options.flavor == Flavor::GoogleCloudTextToSpeech && self.src.is_empty() {
// https://cloud.google.com/text-to-speech/docs/ssml#attributes_1
return Err(crate::error!("GCTTS requires <audio> elements to have a valid `src`."))?;
}
if let Some(AudioRepeat::Times(times)) = &self.repeat {
if times.is_sign_negative() {
return Err(crate::error!("`times` cannot be negative"))?;
}
}
if let Some(speed) = &self.speed {
if speed.is_sign_negative() {
return Err(crate::error!("`speed` cannot be negative"))?;
}
}
}

writer.element("audio", |writer| {
writer.attr("src", &*self.src)?;

Expand Down Expand Up @@ -234,29 +217,3 @@ impl Display for SpeedFormatter {
}
}
impl TrustedNoEscape for SpeedFormatter {}

#[cfg(test)]
mod tests {
use super::{Audio, AudioRepeat};
use crate::{Serialize, SerializeOptions};

#[test]
fn non_negative_speed() {
assert!(
Audio::default()
.with_speed(-1.0)
.serialize_to_string(&SerializeOptions::default())
.is_err()
);
}

#[test]
fn non_negative_repeat_times() {
assert!(
Audio::default()
.with_repeat(AudioRepeat::Times(-1.0))
.serialize_to_string(&SerializeOptions::default())
.is_err()
);
}
}
15 changes: 1 addition & 14 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use alloc::string::String;
use core::{
fmt::{self, Display},
str::Utf8Error
Expand All @@ -13,7 +12,6 @@ pub enum Error {
TimeDesignationError(TimeDesignationError),
DecibelsError(DecibelsError),
AttributesInChildContext,
Generic(String),
Utf8Error(Utf8Error)
}

Expand All @@ -40,8 +38,7 @@ impl Display for Error {
Error::Utf8Error(e) => e.fmt(f),
Error::TimeDesignationError(e) => e.fmt(f),
Error::DecibelsError(e) => e.fmt(f),
Error::AttributesInChildContext => f.write_str("invalid ordering: attempted to write attributes after writing children"),
Error::Generic(s) => f.write_str(s)
Error::AttributesInChildContext => f.write_str("invalid ordering: attempted to write attributes after writing children")
}
}
}
Expand All @@ -50,13 +47,3 @@ impl Display for Error {
impl std::error::Error for Error {}

pub type Result<T, E = Error> = core::result::Result<T, E>;

macro_rules! error {
($m:literal) => {
$crate::Error::Generic(::alloc::format!($m))
};
($fmt:expr, $($arg:tt)*) => {
$crate::Error::Generic(::alloc::format!($fmt, $($arg)*))
};
}
pub(crate) use error;
51 changes: 7 additions & 44 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ extern crate core;

use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec
string::{String, ToString}
};
use core::fmt::{Debug, Write};

Expand All @@ -54,7 +53,6 @@ pub mod visit_mut;
mod voice;
mod xml;

pub(crate) use self::error::error;
pub use self::{
audio::{Audio, AudioRepeat, audio},
r#break::{Break, BreakStrength, breaks},
Expand Down Expand Up @@ -107,17 +105,14 @@ pub struct SerializeOptions {
///
/// Generally, this should only be used for debugging. Some providers may charge per SSML character (not just spoken
/// character), so enabling this option in production may significantly increase costs.
pub pretty: bool,
/// Whether or not to perform compatibility checks with the chosen flavor. This is enabled by default.
pub perform_checks: bool
pub pretty: bool
}

impl Default for SerializeOptions {
fn default() -> Self {
SerializeOptions {
flavor: Flavor::Generic,
pretty: false,
perform_checks: true
pretty: false
}
}
}
Expand All @@ -137,16 +132,6 @@ impl SerializeOptions {
self.flavor = flavor;
self
}

pub fn perform_checks(mut self) -> Self {
self.perform_checks = true;
self
}

pub fn no_checks(mut self) -> Self {
self.perform_checks = false;
self
}
}

/// Trait to support serializing SSML elements.
Expand Down Expand Up @@ -177,29 +162,19 @@ pub trait Serialize {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Meta<'s> {
raw: Cow<'s, str>,
name: Option<Cow<'s, str>>,
restrict_flavor: Option<Vec<Flavor>>
name: Option<Cow<'s, str>>
}

impl<'s> Meta<'s> {
pub fn new(xml: impl Into<Cow<'s, str>>) -> Self {
Meta {
raw: xml.into(),
name: None,
restrict_flavor: None
}
Meta { raw: xml.into(), name: None }
}

pub fn with_name(mut self, name: impl Into<Cow<'s, str>>) -> Self {
self.name = Some(name.into());
self
}

pub fn with_restrict_flavor(mut self, flavors: impl IntoIterator<Item = Flavor>) -> Self {
self.restrict_flavor = Some(flavors.into_iter().collect());
self
}

pub fn to_owned(&self) -> Meta<'static> {
self.clone().into_owned()
}
Expand All @@ -214,25 +189,13 @@ impl<'s> Meta<'s> {
Some(Cow::Borrowed(b)) => Some(Cow::Owned(b.to_string())),
Some(Cow::Owned(b)) => Some(Cow::Owned(b)),
None => None
},
restrict_flavor: self.restrict_flavor
}
}
}
}

impl<'s> Serialize for Meta<'s> {
fn serialize_xml<W: Write>(&self, writer: &mut XmlWriter<W>, options: &SerializeOptions) -> crate::Result<()> {
if options.perform_checks {
if let Some(flavors) = self.restrict_flavor.as_ref() {
if !flavors.iter().any(|f| f == &options.flavor) {
return Err(crate::error!(
"{} cannot be used with {:?}",
if let Some(name) = &self.name { name } else { "this meta element" },
options.flavor
));
}
}
}
fn serialize_xml<W: Write>(&self, writer: &mut XmlWriter<W>, _: &SerializeOptions) -> crate::Result<()> {
writer.raw(&self.raw)
}
}
6 changes: 1 addition & 5 deletions src/mstts/express.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::{string::String, vec::Vec};
use core::fmt::Write;

use crate::{Element, Flavor, Serialize, SerializeOptions, XmlWriter, util};
use crate::{Element, Serialize, SerializeOptions, XmlWriter, util};

/// A generic expression for use in [`Express`]. Contains the name of the expression and the intensity/degree (default
/// `1.0`).
Expand Down Expand Up @@ -200,10 +200,6 @@ impl<'s> From<Express<'s>> for crate::Element<'s> {

impl<'s> Serialize for Express<'s> {
fn serialize_xml<W: Write>(&self, writer: &mut XmlWriter<W>, options: &SerializeOptions) -> crate::Result<()> {
if options.perform_checks && options.flavor != Flavor::MicrosoftAzureCognitiveSpeechServices {
return Err(crate::error!("`mstts::Express` is only supported in ACSS/MSTTS"));
}

writer.element("mstts:express-as", |writer| {
writer.attr("style", &self.expression.0)?;
writer.attr("styledegree", self.expression.1)?;
Expand Down
3 changes: 1 addition & 2 deletions src/mstts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use alloc::{format, string::ToString};
use core::fmt::{self, Display};

use crate::{Flavor, Meta, voice::Voice};
use crate::{Meta, voice::Voice};

pub mod express;
pub use self::express::{Express, express};
Expand Down Expand Up @@ -136,7 +136,6 @@ impl<'s> MicrosoftVoiceExt for Voice<'s> {
0,
Meta::new(format!("<mstts:viseme type=\"{config}\" />"))
.with_name("MicrosoftViseme")
.with_restrict_flavor([Flavor::MicrosoftAzureCognitiveSpeechServices])
.into()
);
self
Expand Down
4 changes: 0 additions & 4 deletions src/speak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ impl<'s> Speak<'s> {

impl<'s> Serialize for Speak<'s> {
fn serialize_xml<W: Write>(&self, writer: &mut XmlWriter<W>, options: &SerializeOptions) -> crate::Result<()> {
if options.perform_checks && self.lang.is_none() && options.flavor == Flavor::MicrosoftAzureCognitiveSpeechServices {
return Err(crate::error!("{:?} requires a language to be set", options.flavor))?;
}

writer.element("speak", |writer| {
if matches!(options.flavor, Flavor::Generic | Flavor::MicrosoftAzureCognitiveSpeechServices) {
writer.attr("version", "1.0")?;
Expand Down

0 comments on commit 6cfd74a

Please sign in to comment.