-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor, add pretty printing, remove anyhow
- Loading branch information
1 parent
d916d5f
commit 9129622
Showing
18 changed files
with
665 additions
and
324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,4 @@ edition = "2021" | |
repository = "https://github.com/pykeio/ssml" | ||
|
||
[dependencies] | ||
anyhow = "1.0" | ||
dyn-clone = "1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
use std::fmt::Debug; | ||
|
||
use dyn_clone::DynClone; | ||
|
||
use crate::{Audio, Meta, Serialize, SerializeOptions, Text, Voice, XmlWriter}; | ||
|
||
macro_rules! el { | ||
( | ||
$(#[$outer:meta])* | ||
pub enum $name:ident { | ||
$( | ||
$(#[$innermeta:meta])* | ||
$variant:ident($inner:ty) | ||
),* | ||
} | ||
) => { | ||
$(#[$outer])* | ||
pub enum $name { | ||
$( | ||
$(#[$innermeta])* | ||
$variant($inner) | ||
),* | ||
} | ||
|
||
$(impl From<$inner> for $name { | ||
fn from(val: $inner) -> $name { | ||
$name::$variant(val) | ||
} | ||
})* | ||
|
||
impl $crate::Serialize for $name { | ||
fn serialize_xml(&self, writer: &mut $crate::XmlWriter<'_>, options: &$crate::SerializeOptions) -> crate::Result<()> { | ||
match self { | ||
$($name::$variant(inner) => inner.serialize_xml(writer, options),)* | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
el! { | ||
/// Represents all SSML elements. | ||
#[derive(Clone, Debug)] | ||
#[non_exhaustive] | ||
pub enum Element { | ||
Text(Text), | ||
Audio(Audio), | ||
Voice(Voice), | ||
Meta(Meta), | ||
/// 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<dyn DynElement>) | ||
// Break(BreakElement), | ||
// Emphasis(EmphasisElement), | ||
// Lang(LangElement), | ||
// Mark(MarkElement), | ||
// Paragraph(ParagraphElement), | ||
// Phoneme(PhonemeElement), | ||
// Prosody(ProsodyElement), | ||
// SayAs(SayAsElement), | ||
// Sub(SubElement), | ||
// Sentence(SentenceElement), | ||
// Voice(VoiceElement), | ||
// Word(WordElement) | ||
} | ||
} | ||
|
||
impl<T: ToString> From<T> for Element { | ||
fn from(value: T) -> Self { | ||
Element::Text(Text(value.to_string())) | ||
} | ||
} | ||
|
||
/// A dynamic element which can be used to implement non-standard SSML elements outside of the `ssml` crate. | ||
/// | ||
/// ``` | ||
/// use ssml::{DynElement, Element, Serialize, SerializeOptions, XmlWriter}; | ||
/// | ||
/// #[derive(Debug, Clone)] | ||
/// pub struct TomfooleryElement { | ||
/// value: f32, | ||
/// children: Vec<Element> | ||
/// } | ||
/// | ||
/// impl TomfooleryElement { | ||
/// // Increase the tomfoolery level of a section of elements. | ||
/// // ... | ||
/// pub fn new<S: Into<Element>, I: IntoIterator<Item = S>>(value: f32, elements: I) -> Self { | ||
/// Self { | ||
/// value, | ||
/// children: elements.into_iter().map(|f| f.into()).collect() | ||
/// } | ||
/// } | ||
/// | ||
/// // not required, but makes your code much cleaner! | ||
/// pub fn into_dyn(self) -> Element { | ||
/// Element::Dyn(Box::new(self)) | ||
/// } | ||
/// } | ||
/// | ||
/// impl DynElement for TomfooleryElement { | ||
/// fn serialize_xml(&self, writer: &mut XmlWriter<'_>, options: &SerializeOptions) -> ssml::Result<()> { | ||
/// writer.element("tomfoolery", |writer| { | ||
/// writer.attr("influence", self.value.to_string())?; | ||
/// ssml::util::serialize_elements(writer, &self.children, options) | ||
/// }) | ||
/// } | ||
/// } | ||
/// | ||
/// # fn main() -> ssml::Result<()> { | ||
/// let doc = ssml::speak( | ||
/// Some("en-US"), | ||
/// [TomfooleryElement::new(2.0, ["Approaching dangerous levels of tomfoolery!"]).into_dyn()] | ||
/// ); | ||
/// let str = doc.serialize_to_string(&ssml::SerializeOptions::default().pretty())?; | ||
/// assert_eq!( | ||
/// str, | ||
/// r#"<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"> | ||
/// <tomfoolery influence="2"> | ||
/// Approaching dangerous levels of tomfoolery! | ||
/// </tomfoolery> | ||
/// </speak>"# | ||
/// ); | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
pub trait DynElement: Debug + DynClone + Send { | ||
/// Serialize this dynamic element into an [`XmlWriter`]. | ||
/// | ||
/// See [`Serialize::serialize_xml`] for more information. | ||
fn serialize_xml(&self, writer: &mut XmlWriter<'_>, options: &SerializeOptions) -> crate::Result<()>; | ||
|
||
/// An optional tag representing this dynamic element. | ||
fn tag_name(&self) -> Option<&str> { | ||
None | ||
} | ||
|
||
/// If this element has children, returns a reference to the vector containing the element's children. | ||
fn children(&self) -> Option<&Vec<Element>> { | ||
None | ||
} | ||
|
||
/// If this element has children, returns a mutable reference to the vector containing the element's children. | ||
fn children_mut(&mut self) -> Option<&mut Vec<Element>> { | ||
None | ||
} | ||
} | ||
|
||
dyn_clone::clone_trait_object!(DynElement); | ||
|
||
impl Serialize for Box<dyn DynElement> { | ||
fn serialize_xml(&self, writer: &mut XmlWriter<'_>, options: &SerializeOptions) -> crate::Result<()> { | ||
DynElement::serialize_xml(self.as_ref(), writer, options)?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,57 @@ | ||
use std::{error::Error, fmt::Display}; | ||
use std::{error::Error as StdError, fmt::Display, io, str::Utf8Error}; | ||
|
||
use crate::{DecibelsError, TimeDesignationError}; | ||
|
||
#[derive(Debug)] | ||
pub(crate) struct GenericError(pub String); | ||
#[non_exhaustive] | ||
pub enum Error { | ||
IoError(io::Error), | ||
TimeDesignationError(TimeDesignationError), | ||
DecibelsError(DecibelsError), | ||
AttributesInChildContext, | ||
Generic(String), | ||
Utf8Error(Utf8Error) | ||
} | ||
|
||
unsafe impl Send for Error {} | ||
|
||
impl Display for GenericError { | ||
macro_rules! impl_from { | ||
($($variant:ident => $t:ty),*) => { | ||
$(impl From<$t> for Error { | ||
fn from(e: $t) -> Self { | ||
Error::$variant(e) | ||
} | ||
})* | ||
}; | ||
} | ||
|
||
impl_from! { | ||
IoError => io::Error, Utf8Error => Utf8Error, TimeDesignationError => TimeDesignationError, DecibelsError => DecibelsError | ||
} | ||
|
||
impl Display for Error { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.write_str(&self.0) | ||
match self { | ||
Error::IoError(e) => e.fmt(f), | ||
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) | ||
} | ||
} | ||
} | ||
|
||
impl Error for GenericError {} | ||
impl StdError for Error {} | ||
|
||
pub type Result<T, E = Error> = std::result::Result<T, E>; | ||
|
||
macro_rules! error { | ||
($m:literal) => { | ||
$crate::GenericError(format!($m)) | ||
$crate::Error::Generic(format!($m)) | ||
}; | ||
($fmt:expr, $($arg:tt)*) => { | ||
$crate::GenericError(format!($fmt, $($arg)*)) | ||
$crate::Error::Generic(format!($fmt, $($arg)*)) | ||
}; | ||
} | ||
pub(crate) use error; |
Oops, something went wrong.