speechmarkdown_rust/formatters/ssml/
amazon_alexa.rs1use crate::ast::{AstNode, NodeType};
2use crate::error::Result;
3use crate::formatters::base::{Formatter, FormatterOptions};
4use crate::formatters::ssml::base::SsmlFormatterBase;
5
6pub struct AmazonAlexaSsmlFormatter {
7 base: SsmlFormatterBase,
8}
9
10impl AmazonAlexaSsmlFormatter {
11 pub fn new(options: FormatterOptions) -> Self {
12 let base = SsmlFormatterBase::new(options);
13
14 Self { base }
15 }
16
17 fn format_amazon_audio(&self, node: &AstNode) -> Result<String> {
19 let src = node.attributes.get("src").unwrap_or(&String::new()).clone();
20
21 let caption = &node.text;
22
23 if caption.is_empty() {
24 Ok(format!("<audio src=\"{}\"/>", src))
25 } else {
26 Ok(format!(
27 "<audio src=\"{}\">\n<desc>{}</desc>\n</audio>",
28 src,
29 self.base.escape_xml(caption)
30 ))
31 }
32 }
33
34 fn format_amazon_emotion(&self, node: &AstNode) -> Result<String> {
36 let emotion_type = match node.node_type {
37 NodeType::Excited => "excited",
38 NodeType::Disappointed => "disappointed",
39 _ => return Ok(String::new()),
40 };
41
42 let intensity = node
43 .attributes
44 .get("value")
45 .unwrap_or(&"medium".to_string())
46 .clone();
47
48 Ok(format!(
49 "<amazon:emotion name=\"{}\" intensity=\"{}\">",
50 emotion_type, intensity
51 ))
52 }
53
54 fn format_amazon_domain(&self, node: &AstNode) -> Result<String> {
56 let domain_name = match node.node_type {
57 NodeType::Newscaster => "news",
58 NodeType::Dj => "music",
59 _ => return Ok(String::new()),
60 };
61
62 Ok(format!("<amazon:domain name=\"{}\">", domain_name))
63 }
64}
65
66impl Formatter for AmazonAlexaSsmlFormatter {
67 fn format(&self, ast: &AstNode) -> Result<String> {
68 self.base.format(ast)
69 }
70
71 fn format_node(&self, node: &AstNode) -> Result<String> {
72 match node.node_type {
73 NodeType::Audio => self.format_amazon_audio(node),
75
76 NodeType::Excited | NodeType::Disappointed => self.format_amazon_emotion(node),
78
79 NodeType::Newscaster | NodeType::Dj => self.format_amazon_domain(node),
81
82 _ => self.base.format_node(node),
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::parser::SpeechMarkdownParser;
91
92 #[test]
93 fn test_amazon_alexa_basic_parsing() {
94 let input = "Hello world";
95 let result =
96 SpeechMarkdownParser::to_ssml(input, crate::formatters::base::Platform::AmazonAlexa);
97 assert!(result.is_ok());
98 }
99
100 #[test]
101 fn test_amazon_alexa_with_break() {
102 let input = "Sample [2s] text";
103 let result =
104 SpeechMarkdownParser::to_ssml(input, crate::formatters::base::Platform::AmazonAlexa);
105 assert!(result.is_ok());
106
107 let ssml = result.unwrap();
108 assert!(ssml.contains("<break time=\"2s\"/>"));
109 }
110
111 #[test]
112 fn test_amazon_alexa_with_emphasis() {
113 let input = "++strong emphasis++";
114 let result =
115 SpeechMarkdownParser::to_ssml(input, crate::formatters::base::Platform::AmazonAlexa);
116 assert!(result.is_ok());
117
118 let ssml = result.unwrap();
119 assert!(ssml.contains("<emphasis level=\"strong\">"));
120 }
121}