text_to_polly_ssml/xml_writer.rs
1//! Controls writing of the XML part of SSML. This contains all low level bindings in a sense
2//! to the tags. You should probably never use this directly.
3
4use color_eyre::{eyre::eyre, Result};
5use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
6use quick_xml::Writer;
7
8use std::io::Cursor;
9
10use crate::ssml_constants::*;
11
12/// An XML Writer. Used for manual manipulation of the SSML Output (which uses XML).
13///
14/// You should probably never use this directly, instead interacting with the parser,
15/// however if you'd like to build your own parser, and just reuse the XML Rendering
16/// then you'd want to use this.
17pub struct XmlWriter {
18 /// The XML Writer instance. The thing that actually writes the XML.
19 pub writer: Writer<Cursor<Vec<u8>>>,
20}
21
22impl XmlWriter {
23 /// Creates a new XML Writer. This writerr writes into a std::vec::Vec, and at any
24 /// point can be turned into a string. It is your job to close all tags before rendering
25 /// this. We don't close everything when you render it. You render what you put in.
26 ///
27 /// It should also note we automatically write the header:
28 ///
29 /// ```text
30 /// <?xml version="1.0"?>
31 /// ```
32 ///
33 /// Upon creation of an XML Writer. This is to try, and keep as close to the W3C docs
34 /// for SSML v1.1. Which you can read about
35 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/).
36 ///
37 /// # Examples
38 ///
39 /// ```rust
40 /// use text_to_polly_ssml::xml_writer::XmlWriter;
41 /// let result = XmlWriter::new();
42 /// assert!(result.is_ok());
43 /// ```
44 pub fn new() -> Result<XmlWriter> {
45 let mut writer = Writer::new(Cursor::new(Vec::new()));
46 writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
47 Ok(XmlWriter { writer: writer })
48 }
49
50 /// Starts an SSML <speak> tag. For AWS Polly this is the root tag, and should only have one
51 /// decleration as mentioned in their docs (As of April 20th, 2017):
52 ///
53 /// ```text
54 /// The <speak> tag is the root element of all Amazon Polly SSML text.
55 /// All SSML-enhanced text to be spoken must be included within this tag.
56 /// ```
57 ///
58 /// It should be noted although AWS Docs do not mention any attributes you can pass in
59 /// to the <speak> tag, we still have an optional `lang`, and `onlangfailure` attributes
60 /// to closely mirror the W3C Standard, as seen:
61 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/).
62 ///
63 ///
64 /// # Examples
65 ///
66 /// Rust Code:
67 ///
68 /// ```rust
69 /// use text_to_polly_ssml::xml_writer::XmlWriter;
70 /// let mut new_xml_writer = XmlWriter::new();
71 /// assert!(new_xml_writer.is_ok());
72 /// let start_speak_result = new_xml_writer.unwrap().start_ssml_speak(None, None);
73 /// assert!(start_speak_result.is_ok());
74 /// ```
75 ///
76 /// Generated SSML:
77 ///
78 /// ```text
79 /// <?xml version="1.0"?>
80 /// <speak xml:lang="en-US" onlangfailure="processorchoice"
81 /// xmlns="http://www.w3.org/2001/10/synthesis"
82 /// xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
83 /// ```
84 pub fn start_ssml_speak(
85 &mut self,
86 lang: Option<String>,
87 onlangfailure: Option<String>,
88 ) -> Result<()> {
89 let mut elem = BytesStart::owned(b"speak".to_vec(), "speak".len());
90 elem.push_attribute(("xml:lang", &*lang.unwrap_or("en-US".to_owned())));
91 elem.push_attribute((
92 "onlangfailure",
93 &*onlangfailure.unwrap_or("processorchoice".to_owned()),
94 ));
95 elem.push_attribute(("xmlns", "http://www.w3.org/2001/10/synthesis"));
96 elem.push_attribute(("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"));
97 Ok(self.writer.write_event(Event::Start(elem))?)
98 }
99
100 /// Ends an SSML <speak> tag. For AWS Polly this should be the root tag, and you
101 /// should only close it when you are done.
102 ///
103 /// # Examples
104 ///
105 /// Rust Code:
106 ///
107 /// ```rust
108 /// use text_to_polly_ssml::xml_writer::XmlWriter;
109 /// let mut new_xml_writer = XmlWriter::new();
110 /// assert!(new_xml_writer.is_ok());
111 /// let end_speak_result = new_xml_writer.unwrap().end_ssml_speak();
112 /// assert!(end_speak_result.is_ok());
113 /// ```
114 ///
115 /// Generated SSML:
116 ///
117 /// ```text
118 /// <?xml version="1.0"?>
119 /// </speak>
120 /// ```
121 pub fn end_ssml_speak(&mut self) -> Result<()> {
122 Ok(self
123 .writer
124 .write_event(Event::End(BytesEnd::borrowed(b"speak")))?)
125 }
126
127 /// Creates an SSML <break> tag. AWS Polly follows the W3C SSMLv1.1 standard for
128 /// this tag.
129 ///
130 /// You can find the SSML <break> tag documented in the W3C's guide:
131 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/).
132 /// Although you can find the specific implementation details on AWS's site:
133 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#break-tag).
134 ///
135 /// According to the W3C 1.1 Standard both the strength, and time are optional.
136 /// Though both can be used in combination.
137 ///
138 /// # Examples
139 ///
140 /// Rust Code:
141 ///
142 /// ```rust
143 /// use text_to_polly_ssml::xml_writer::XmlWriter;
144 /// let mut new_xml_writer = XmlWriter::new();
145 /// assert!(new_xml_writer.is_ok());
146 /// let mut xml_writer = new_xml_writer.unwrap();
147 /// let result = xml_writer.ssml_break(None, None);
148 /// assert!(result.is_ok());
149 /// ```
150 ///
151 /// Generated SSML:
152 ///
153 /// ```text
154 /// <?xml version="1.0"?>
155 /// <break />
156 /// ```
157 ///
158 /// ---
159 ///
160 /// Rust Code:
161 ///
162 /// ```rust
163 /// use text_to_polly_ssml::xml_writer::XmlWriter;
164 /// use text_to_polly_ssml::ssml_constants::{BreakStrength, BreakTime};
165 /// let mut new_xml_writer = XmlWriter::new();
166 /// assert!(new_xml_writer.is_ok());
167 /// let mut xml_writer = new_xml_writer.unwrap();
168 /// let result = xml_writer.ssml_break(Some(BreakStrength::XStrong), Some(BreakTime::new(10, true)));
169 /// assert!(result.is_ok());
170 /// ```
171 ///
172 /// Generated SSML:
173 ///
174 /// ```text
175 /// <?xml version="1.0"?>
176 /// <break strength="x-strong" time="10s" />
177 /// ```
178 pub fn ssml_break(
179 &mut self,
180 strength: Option<BreakStrength>,
181 time: Option<BreakTime>,
182 ) -> Result<()> {
183 let mut elem = BytesStart::owned(b"break".to_vec(), "break".len());
184
185 if strength.is_some() {
186 elem.push_attribute(("strength", &*format!("{}", strength.unwrap())));
187 }
188 if time.is_some() {
189 elem.push_attribute(("time", &*format!("{}", time.unwrap())));
190 }
191
192 Ok(self.writer.write_event(Event::Empty(elem))?)
193 }
194
195 /// Starts an SSML Lang tag. The Lang tag is useful for telling say
196 /// someone speaking in english that they're about to speak a french word. You can keep
197 /// the overall text english, but have a mix of french words in there. Although AWS polly
198 /// only documents support the `xml:lang` attribute, we also pass in `onlangfailure`
199 /// which is documented inside the W3C SSML 1.1 standard which can be found:
200 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_lang).
201 ///
202 /// You can find the AWS Documentation that mentions the lang tag:
203 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#lang-tag).
204 ///
205 /// # Examples
206 ///
207 /// Rust Code:
208 ///
209 /// ```rust
210 /// use text_to_polly_ssml::xml_writer::XmlWriter;
211 /// let mut new_xml_writer = XmlWriter::new();
212 /// assert!(new_xml_writer.is_ok());
213 /// let start_lang_result = new_xml_writer.unwrap().start_ssml_lang("fr-FR".to_owned(), None);
214 /// assert!(start_lang_result.is_ok());
215 /// ```
216 ///
217 /// Generated SSML:
218 ///
219 /// ```text
220 /// <?xml version="1.0"?>
221 /// <lang xml:lang="fr-FR" onlangfailure="processorchoice">
222 /// ```
223 ///
224 /// ---
225 ///
226 /// Rust Code:
227 /// ```rust
228 /// use text_to_polly_ssml::xml_writer::XmlWriter;
229 /// let mut new_xml_writer = XmlWriter::new();
230 /// assert!(new_xml_writer.is_ok());
231 /// let start_lang_result = new_xml_writer.unwrap().start_ssml_lang("fr-FR".to_owned(),
232 /// Some("changevoice".to_owned()));
233 /// assert!(start_lang_result.is_ok());
234 /// ```
235 ///
236 /// Generated SSML:
237 ///
238 /// ```text
239 /// <?xml version="1.0"?>
240 /// <lang xml:lang="fr-FR" onlangfailure="changevoice">
241 /// ```
242 pub fn start_ssml_lang(&mut self, lang: String, onlangfailure: Option<String>) -> Result<()> {
243 let mut elem = BytesStart::owned(b"lang".to_vec(), "lang".len());
244 elem.push_attribute(("xml:lang", &*lang));
245 elem.push_attribute((
246 "onlangfailure",
247 &*onlangfailure.unwrap_or("processorchoice".to_owned()),
248 ));
249 Ok(self.writer.write_event(Event::Start(elem))?)
250 }
251
252 /// Ends an SSML <lang> tag.
253 ///
254 /// # Examples
255 ///
256 /// Rust Code:
257 ///
258 /// ```rust
259 /// use text_to_polly_ssml::xml_writer::XmlWriter;
260 /// let mut new_xml_writer = XmlWriter::new();
261 /// assert!(new_xml_writer.is_ok());
262 /// let end_lang_result = new_xml_writer.unwrap().end_ssml_lang();
263 /// assert!(end_lang_result.is_ok());
264 /// ```
265 ///
266 /// Generated SSML:
267 ///
268 /// ```text
269 /// <?xml version="1.0"?>
270 /// </lang>
271 /// ```
272 pub fn end_ssml_lang(&mut self) -> Result<()> {
273 Ok(self
274 .writer
275 .write_event(Event::End(BytesEnd::borrowed(b"lang")))?)
276 }
277
278 /// Starts an SSML Mark tag. Although this will make no difference in the voice
279 /// of the text, this will place a marker inside the SSML Metadata returned from Polly.
280 /// This can be useful if you want to perform some sort of actions on certain words.
281 /// AWS Polly follows the W3C SSML v1.1 Spec here, and documentation can be found:
282 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_mark).
283 ///
284 /// You can find the AWS Documentation that mentions the mark tag:
285 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#custom-tag).
286 ///
287 /// # Examples
288 ///
289 /// Rust Code:
290 ///
291 /// ```rust
292 /// use text_to_polly_ssml::xml_writer::XmlWriter;
293 /// let mut new_xml_writer = XmlWriter::new();
294 /// assert!(new_xml_writer.is_ok());
295 /// let start_mark_result = new_xml_writer.unwrap().start_ssml_mark("animal".to_owned());
296 /// assert!(start_mark_result.is_ok());
297 /// ```
298 ///
299 /// Generated SSML:
300 ///
301 /// ```text
302 /// <?xml version="1.0"?>
303 /// Mmark name="animal">
304 /// ```
305 pub fn start_ssml_mark(&mut self, name: String) -> Result<()> {
306 let mut elem = BytesStart::owned(b"mark".to_vec(), "mark".len());
307 elem.push_attribute(("name", &*name));
308 Ok(self.writer.write_event(Event::Start(elem))?)
309 }
310
311 /// Ends an SSML <mark> tag.
312 ///
313 /// # Examples
314 ///
315 /// Rust Code:
316 ///
317 /// ```rust
318 /// use text_to_polly_ssml::xml_writer::XmlWriter;
319 /// let mut new_xml_writer = XmlWriter::new();
320 /// assert!(new_xml_writer.is_ok());
321 /// let end_mark_result = new_xml_writer.unwrap().end_ssml_mark();
322 /// assert!(end_mark_result.is_ok());
323 /// ```
324 ///
325 /// Generated SSML:
326 ///
327 /// ```text
328 /// <?xml version="1.0"?>
329 /// </mark>
330 /// ```
331 pub fn end_ssml_mark(&mut self) -> Result<()> {
332 Ok(self
333 .writer
334 .write_event(Event::End(BytesEnd::borrowed(b"mark")))?)
335 }
336
337 /// Starts an SSML Paragraph Tag. The Paragraph Tag is useful for breaking
338 /// up multiple paragraphs of text. AWS Polly follows the W3C SSML v1.1 Standard Here.
339 /// As such the documentation for the paragraph tag can be found:
340 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_paragraph).
341 ///
342 /// You can find the AWS Documentation that mentions the paragraph tag:
343 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#p-tag).
344 ///
345 /// # Examples
346 ///
347 /// Rust Code:
348 ///
349 /// ```rust
350 /// use text_to_polly_ssml::xml_writer::XmlWriter;
351 /// let mut new_xml_writer = XmlWriter::new();
352 /// assert!(new_xml_writer.is_ok());
353 /// let start_p_result = new_xml_writer.unwrap().start_ssml_paragraph();
354 /// assert!(start_p_result.is_ok());
355 /// ```
356 ///
357 /// Generated SSML:
358 ///
359 /// ```text
360 /// <?xml version="1.0"?>
361 /// <p>
362 /// ```
363 pub fn start_ssml_paragraph(&mut self) -> Result<()> {
364 Ok(self
365 .writer
366 .write_event(Event::Start(BytesStart::owned(b"p".to_vec(), "p".len())))?)
367 }
368
369 /// Ends an SSML <p> tag.
370 ///
371 /// # Examples
372 ///
373 /// Rust Code:
374 ///
375 /// ```rust
376 /// use text_to_polly_ssml::xml_writer::XmlWriter;
377 /// let mut new_xml_writer = XmlWriter::new();
378 /// assert!(new_xml_writer.is_ok());
379 /// let end_p_result = new_xml_writer.unwrap().end_ssml_paragraph();
380 /// assert!(end_p_result.is_ok());
381 /// ```
382 ///
383 /// Generated SSML:
384 ///
385 /// ```text
386 /// <?xml version="1.0"?>
387 /// </p>
388 /// ```
389 pub fn end_ssml_paragraph(&mut self) -> Result<()> {
390 Ok(self
391 .writer
392 .write_event(Event::End(BytesEnd::borrowed(b"p")))?)
393 }
394
395 /// Starts an SSML Phoneme Tag. The Phoneme Tag is useful for custom pronunciation for words.
396 /// The Phoneme Tag should really only be used on a per word/short phrase basis. You don't
397 /// want to use a phoneme tag for an entire paragraph of text. The Phoneme Tag in polly
398 /// has two required attributes both "ph", and "alphabet". Which deviates from the W3C Standard,
399 /// which says only "ph" is required. However since Polly implements close to perfect the W3C
400 /// SSML v1.1 Standard here you should still probably read their documentation on the tag:
401 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_phoneme).
402 ///
403 /// You can find the AWS Documentation that mentions the phoneme tag:
404 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#phoneme-tag).
405 ///
406 /// # Examples
407 ///
408 /// Rust Code:
409 ///
410 /// ```rust
411 /// use text_to_polly_ssml::xml_writer::XmlWriter;
412 /// use text_to_polly_ssml::ssml_constants::PhonemeAlphabet;
413 /// let mut new_xml_writer = XmlWriter::new();
414 /// assert!(new_xml_writer.is_ok());
415 /// let start_phoneme_result = new_xml_writer.unwrap().start_ssml_phoneme(PhonemeAlphabet::Ipa,
416 /// "d͡ʒt͡ʃΘɚoʊɛ".to_owned());
417 /// assert!(start_phoneme_result.is_ok());
418 /// ```
419 ///
420 /// Generated SSML:
421 ///
422 /// ```text
423 /// <?xml version="1.0"?>
424 /// <phoneme alphabet="ipa" ph="d͡ʒt͡ʃΘɚoʊɛ">
425 /// ```
426 pub fn start_ssml_phoneme(&mut self, alphabet: PhonemeAlphabet, ph: String) -> Result<()> {
427 let mut elem = BytesStart::owned(b"phoneme".to_vec(), "phoneme".len());
428 elem.push_attribute(("alphabet", &*format!("{}", alphabet)));
429 elem.push_attribute(("ph", &*ph));
430 Ok(self.writer.write_event(Event::Start(elem))?)
431 }
432
433 /// Ends an SSML <phoneme> tag.
434 ///
435 /// # Examples
436 ///
437 /// Rust Code:
438 ///
439 /// ```rust
440 /// use text_to_polly_ssml::xml_writer::XmlWriter;
441 /// let mut new_xml_writer = XmlWriter::new();
442 /// assert!(new_xml_writer.is_ok());
443 /// let end_phoneme_result = new_xml_writer.unwrap().end_ssml_phoneme();
444 /// assert!(end_phoneme_result.is_ok());
445 /// ```
446 ///
447 /// Generated SSML:
448 ///
449 /// ```text
450 /// <?xml version="1.0"?>
451 /// </phoneme>
452 /// ```
453 pub fn end_ssml_phoneme(&mut self) -> Result<()> {
454 Ok(self
455 .writer
456 .write_event(Event::End(BytesEnd::borrowed(b"phoneme")))?)
457 }
458
459 /// Starts an SSML Prosody Tag. The prosody tag seems to be the one that derives the most
460 /// from the SSML Specification. Which in some instances is fine because it makes for easier
461 /// reading (e.g. +20% pitch), but in other places is kind of sad we can't do that. (e.g.
462 /// things like duration). As such I'll only link to the AWS documentation.
463 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#prosody-tag).
464 ///
465 /// # Examples
466 ///
467 /// Rust Code:
468 ///
469 /// ```rust
470 /// use text_to_polly_ssml::xml_writer::XmlWriter;
471 /// let mut new_xml_writer = XmlWriter::new();
472 /// assert!(new_xml_writer.is_ok());
473 /// let start_prosody_result = new_xml_writer.unwrap().start_ssml_prosody(Some("+5db".to_owned()), None, None);
474 /// assert!(start_prosody_result.is_ok());
475 /// ```
476 ///
477 /// Generated SSML:
478 ///
479 /// ```text
480 /// <?xml version="1.0"?>
481 /// <prosody volume="+6db">
482 /// ```
483 ///
484 /// ---
485 ///
486 /// Rust Code:
487 ///
488 /// ```rust
489 /// use text_to_polly_ssml::xml_writer::XmlWriter;
490 /// use text_to_polly_ssml::ssml_constants::ProsodyRate;
491 /// let mut new_xml_writer = XmlWriter::new();
492 /// assert!(new_xml_writer.is_ok());
493 /// let start_prosody_result = new_xml_writer.unwrap()
494 /// .start_ssml_prosody(Some("+6dB".to_owned()), Some(ProsodyRate::XFast),
495 /// Some("+100%".to_owned()));
496 /// assert!(start_prosody_result.is_ok());
497 /// ```
498 ///
499 /// Generated SSML:
500 ///
501 /// ```text
502 /// <?xml version="1.0"?>
503 /// <prosody volume="+6db" rate="x-fast" pitch="+100%">
504 /// ```
505 pub fn start_ssml_prosody(
506 &mut self,
507 volume: Option<String>,
508 rate: Option<ProsodyRate>,
509 pitch: Option<String>,
510 ) -> Result<()> {
511 let mut elem = BytesStart::owned(b"prosody".to_vec(), "prosody".len());
512 if volume.is_none() && rate.is_none() && pitch.is_none() {
513 return Err(eyre!("Prosody Tag was supplied no values."));
514 }
515 if volume.is_some() {
516 elem.push_attribute(("volume", &*volume.unwrap()));
517 }
518 if rate.is_some() {
519 elem.push_attribute(("rate", &*format!("{}", rate.unwrap())));
520 }
521 if pitch.is_some() {
522 elem.push_attribute(("pitch", &*pitch.unwrap()));
523 }
524 Ok(self.writer.write_event(Event::Start(elem))?)
525 }
526
527 /// Ends an SSML <prosody> tag.
528 ///
529 /// # Examples
530 ///
531 /// Rust Code:
532 ///
533 /// ```rust
534 /// use text_to_polly_ssml::xml_writer::XmlWriter;
535 /// let mut new_xml_writer = XmlWriter::new();
536 /// assert!(new_xml_writer.is_ok());
537 /// let end_prosody_result = new_xml_writer.unwrap().end_ssml_prosody();
538 /// assert!(end_prosody_result.is_ok());
539 /// ```
540 ///
541 /// Generated SSML:
542 ///
543 /// ```text
544 /// <?xml version="1.0"?>
545 /// </prosody>
546 /// ```
547 pub fn end_ssml_prosody(&mut self) -> Result<()> {
548 Ok(self
549 .writer
550 .write_event(Event::End(BytesEnd::borrowed(b"prosody")))?)
551 }
552
553 /// Starts an SSML Sentence Tag. The Sentence Tag is useful for breaking
554 /// up multiple sentences of text. AWS Polly follows the W3C SSML v1.1 Standard Here.
555 /// As such the documentation for the sentence tag can be found:
556 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_sentence).
557 ///
558 /// You can find the AWS Documentation that mentions the sentence tag:
559 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#s-tag).
560 ///
561 /// # Examples
562 ///
563 /// Rust Code:
564 ///
565 /// ```rust
566 /// use text_to_polly_ssml::xml_writer::XmlWriter;
567 /// let mut new_xml_writer = XmlWriter::new();
568 /// assert!(new_xml_writer.is_ok());
569 /// let start_s_result = new_xml_writer.unwrap().start_ssml_sentence();
570 /// assert!(start_s_result.is_ok());
571 /// ```
572 ///
573 /// Generated SSML:
574 ///
575 /// ```text
576 /// <?xml version="1.0"?>
577 /// <s>
578 /// ```
579 pub fn start_ssml_sentence(&mut self) -> Result<()> {
580 Ok(self
581 .writer
582 .write_event(Event::Start(BytesStart::owned(b"s".to_vec(), "s".len())))?)
583 }
584
585 /// Ends an SSML <s> tag.
586 ///
587 /// # Examples
588 ///
589 /// Rust Code:
590 ///
591 /// ```rust
592 /// use text_to_polly_ssml::xml_writer::XmlWriter;
593 /// let mut new_xml_writer = XmlWriter::new();
594 /// assert!(new_xml_writer.is_ok());
595 /// let end_s_result = new_xml_writer.unwrap().end_ssml_sentence();
596 /// assert!(end_s_result.is_ok());
597 /// ```
598 ///
599 /// Generated SSML:
600 ///
601 /// ```text
602 /// <?xml version="1.0"?>
603 /// </s>
604 /// ```
605 pub fn end_ssml_sentence(&mut self) -> Result<()> {
606 Ok(self
607 .writer
608 .write_event(Event::End(BytesEnd::borrowed(b"s")))?)
609 }
610
611 /// Starts an SSML say-as Tag. The say-as tag is used for determing how a body of text
612 /// should be interpreted, for example a phone number, or if you want something spelled
613 /// out letter by letter. However AWS polly only supports the `interpret-as` attribute
614 /// which is required, and does not support the `format`, and `detail` attributes.
615 /// However for posterity you can read the W3C SSML v1.1 Spec:
616 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_say-as).
617 /// It should be noted the parameter for interpret-as is kept dynamic, since in the
618 /// spec it says this list ***should*** change rapidly.
619 ///
620 /// You can find the AWS Documentation that mentions the say-as tag:
621 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#say-as-tag).
622 ///
623 /// # Examples
624 ///
625 /// Rust Code:
626 ///
627 /// ```rust
628 /// use text_to_polly_ssml::xml_writer::XmlWriter;
629 /// let mut new_xml_writer = XmlWriter::new();
630 /// assert!(new_xml_writer.is_ok());
631 /// let start_say_as_result = new_xml_writer.unwrap().start_ssml_say_as("character".to_owned());
632 /// assert!(start_say_as_result.is_ok());
633 /// ```
634 ///
635 /// Generated SSML:
636 ///
637 /// ```text
638 /// <?xml version="1.0"?>
639 /// <say-as interpret-as="character">
640 /// ```
641 pub fn start_ssml_say_as(&mut self, interpret_as: String) -> Result<()> {
642 let mut elem = BytesStart::owned(b"say-as".to_vec(), "say-as".len());
643 elem.push_attribute(("interpret-as", &*interpret_as));
644 Ok(self.writer.write_event(Event::Start(elem))?)
645 }
646
647 /// Ends an SSML <say-as> tag.
648 ///
649 /// # Examples
650 ///
651 /// Rust Code:
652 ///
653 /// ```rust
654 /// use text_to_polly_ssml::xml_writer::XmlWriter;
655 /// let mut new_xml_writer = XmlWriter::new();
656 /// assert!(new_xml_writer.is_ok());
657 /// let end_say_as_result = new_xml_writer.unwrap().end_ssml_say_as();
658 /// assert!(end_say_as_result.is_ok());
659 /// ```
660 ///
661 /// Generated SSML:
662 ///
663 /// ```text
664 /// <?xml version="1.0"?>
665 /// </say-as>
666 /// ```
667 pub fn end_ssml_say_as(&mut self) -> Result<()> {
668 Ok(self
669 .writer
670 .write_event(Event::End(BytesEnd::borrowed(b"say-as")))?)
671 }
672
673 /// Starts an SSML sub Tag. The sub tag is used for a substitution of a word.
674 /// For example in elememtal symbols you may want to show the elememtal symbol, but have
675 /// the engine say the actual element name. AWS polly follows the W3C SSML v1.1 Spec:
676 /// [HERE](https://www.w3.org/TR/2010/REC-speech-synthesis11-20100907/#edef_sub).
677 ///
678 /// You can find the AWS Documentation that mentions the sub tag:
679 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#sub-tag).
680 ///
681 /// # Examples
682 ///
683 /// Rust Code:
684 ///
685 /// ```rust
686 /// use text_to_polly_ssml::xml_writer::XmlWriter;
687 /// let mut new_xml_writer = XmlWriter::new();
688 /// assert!(new_xml_writer.is_ok());
689 /// let start_sub_result = new_xml_writer.unwrap().start_ssml_sub("mercury".to_owned());
690 /// assert!(start_sub_result.is_ok());
691 /// ```
692 ///
693 /// Generated SSML:
694 ///
695 /// ```text
696 /// <?xml version="1.0"?>
697 /// <sub alias="mercury">
698 /// ```
699 pub fn start_ssml_sub(&mut self, alias: String) -> Result<()> {
700 let mut elem = BytesStart::owned(b"sub".to_vec(), "sub".len());
701 elem.push_attribute(("alias", &*alias));
702 Ok(self.writer.write_event(Event::Start(elem))?)
703 }
704
705 /// Ends an SSML <sub> tag.
706 ///
707 /// # Examples
708 ///
709 /// Rust Code:
710 ///
711 /// ```rust
712 /// use text_to_polly_ssml::xml_writer::XmlWriter;
713 /// let mut new_xml_writer = XmlWriter::new();
714 /// assert!(new_xml_writer.is_ok());
715 /// let end_sub_result = new_xml_writer.unwrap().end_ssml_sub();
716 /// assert!(end_sub_result.is_ok());
717 /// ```
718 ///
719 /// Generated SSML:
720 ///
721 /// ```text
722 /// <?xml version="1.0"?>
723 /// </sub>
724 /// ```
725 pub fn end_ssml_sub(&mut self) -> Result<()> {
726 Ok(self
727 .writer
728 .write_event(Event::End(BytesEnd::borrowed(b"sub")))?)
729 }
730
731 /// Starts an SSML Word/Token tag. The Word/Token tag for AWS Polly also deviates pretty
732 /// far from the W3C Spec. So here like a few tags who shall not be named I will also
733 /// only ilnk to the AWS Documentation for this tag. Which can be found:
734 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html#w-tag).
735 ///
736 /// # Examples
737 ///
738 /// Rust Code:
739 ///
740 /// ```rust
741 /// use text_to_polly_ssml::xml_writer::XmlWriter;
742 /// use text_to_polly_ssml::ssml_constants::WordRole;
743 /// let mut new_xml_writer = XmlWriter::new();
744 /// assert!(new_xml_writer.is_ok());
745 /// let start_w_result = new_xml_writer.unwrap().start_ssml_w(WordRole::Verb);
746 /// assert!(start_w_result.is_ok());
747 /// ```
748 ///
749 /// Generated SSML:
750 ///
751 /// ```text
752 /// <?xml version="1.0"?>
753 /// <w role="amazon:VB">
754 /// ```
755 pub fn start_ssml_w(&mut self, role: WordRole) -> Result<()> {
756 let mut elem = BytesStart::owned(b"w".to_vec(), "w".len());
757 elem.push_attribute(("role", &*format!("{}", role)));
758 Ok(self.writer.write_event(Event::Start(elem))?)
759 }
760
761 /// Ends an SSML <w> tag.
762 ///
763 /// # Examples
764 ///
765 /// Rust Code:
766 ///
767 /// ```rust
768 /// use text_to_polly_ssml::xml_writer::XmlWriter;
769 /// let mut new_xml_writer = XmlWriter::new();
770 /// assert!(new_xml_writer.is_ok());
771 /// let end_w_result = new_xml_writer.unwrap().end_ssml_w();
772 /// assert!(end_w_result.is_ok());
773 /// ```
774 ///
775 /// Generated SSML:
776 ///
777 /// ```text
778 /// <?xml version="1.0"?>
779 /// </w>
780 /// ```
781 pub fn end_ssml_w(&mut self) -> Result<()> {
782 Ok(self
783 .writer
784 .write_event(Event::End(BytesEnd::borrowed(b"w")))?)
785 }
786
787 /// Starts an SSML amazon domain tag. These tags are unique to AWS Polly. As such
788 /// the only place they are documented is inside the AWS Docs themsleves which are:
789 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html).
790 ///
791 /// # Examples
792 ///
793 /// Rust Code:
794 ///
795 /// ```rust
796 /// use text_to_polly_ssml::xml_writer::XmlWriter;
797 /// use text_to_polly_ssml::ssml_constants::AmazonDomainNames;
798 /// let mut new_xml_writer = XmlWriter::new();
799 /// assert!(new_xml_writer.is_ok());
800 /// let start_amazon_effect_result = new_xml_writer.unwrap()
801 /// .start_ssml_amazon_domain(AmazonDomainNames::News);
802 /// assert!(start_amazon_effect_result.is_ok());
803 /// ```
804 ///
805 /// Generated SSML:
806 ///
807 /// ```text
808 /// <?xml version="1.0"?>
809 /// <amazon:domain name="news">
810 /// ```
811 pub fn start_ssml_amazon_domain(&mut self, name: AmazonDomainNames) -> Result<()> {
812 let mut elem = BytesStart::owned(b"amazon:domain".to_vec(), "amazon:domain".len());
813 elem.push_attribute(("name", &*format!("{}", name)));
814 Ok(self.writer.write_event(Event::Start(elem))?)
815 }
816
817 /// Ends an SSML <amazon:domain> tag.
818 ///
819 /// # Examples
820 ///
821 /// Rust Code:
822 ///
823 /// ```rust
824 /// use text_to_polly_ssml::xml_writer::XmlWriter;
825 /// let mut new_xml_writer = XmlWriter::new();
826 /// assert!(new_xml_writer.is_ok());
827 /// let end_amazon_effect_result = new_xml_writer.unwrap().end_ssml_amazon_domain();
828 /// assert!(end_amazon_effect_result.is_ok());
829 /// ```
830 ///
831 /// Generated SSML:
832 ///
833 /// ```text
834 /// <?xml version="1.0"?>
835 /// </amazon:domain>
836 /// ```
837 pub fn end_ssml_amazon_domain(&mut self) -> Result<()> {
838 Ok(self
839 .writer
840 .write_event(Event::End(BytesEnd::borrowed(b"amazon:domain")))?)
841 }
842
843 /// Starts an SSML amazon effect tag. These tags are unique to AWS Polly. As such
844 /// the only place they are documented is inside the AWS Docs themsleves which are:
845 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html).
846 ///
847 /// # Examples
848 ///
849 /// Rust Code:
850 ///
851 /// ```rust
852 /// use text_to_polly_ssml::xml_writer::XmlWriter;
853 /// use text_to_polly_ssml::ssml_constants::AmazonEffect;
854 /// let mut new_xml_writer = XmlWriter::new();
855 /// assert!(new_xml_writer.is_ok());
856 /// let start_amazon_effect_result = new_xml_writer.unwrap()
857 /// .start_ssml_amazon_effect(AmazonEffect::Whispered);
858 /// assert!(start_amazon_effect_result.is_ok());
859 /// ```
860 ///
861 /// Generated SSML:
862 ///
863 /// ```text
864 /// <?xml version="1.0"?>
865 /// <amazon:effect name="whispered">
866 /// ```
867 pub fn start_ssml_amazon_effect(&mut self, name: AmazonEffect) -> Result<()> {
868 let mut elem = BytesStart::owned(b"amazon:effect".to_vec(), "amazon:effect".len());
869 elem.push_attribute(("name", &*format!("{}", name)));
870 Ok(self.writer.write_event(Event::Start(elem))?)
871 }
872
873 /// Ends an SSML <amazon:effect> tag.
874 ///
875 /// # Examples
876 ///
877 /// Rust Code:
878 ///
879 /// ```rust
880 /// use text_to_polly_ssml::xml_writer::XmlWriter;
881 /// let mut new_xml_writer = XmlWriter::new();
882 /// assert!(new_xml_writer.is_ok());
883 /// let end_amazon_effect_result = new_xml_writer.unwrap().end_ssml_amazon_effect();
884 /// assert!(end_amazon_effect_result.is_ok());
885 /// ```
886 ///
887 /// Generated SSML:
888 ///
889 /// ```text
890 /// <?xml version="1.0"?>
891 /// </amazon:effect>
892 /// ```
893 pub fn end_ssml_amazon_effect(&mut self) -> Result<()> {
894 Ok(self
895 .writer
896 .write_event(Event::End(BytesEnd::borrowed(b"amazon:effect")))?)
897 }
898
899 /// Starts an SSML vocal tract tag. These tags are unique to AWS Polly. As such
900 /// the only place they are documented is inside the AWS Docs themsleves which are:
901 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html).
902 ///
903 /// # Examples
904 ///
905 /// Rust Code:
906 ///
907 /// ```rust
908 /// use text_to_polly_ssml::xml_writer::XmlWriter;
909 /// let mut new_xml_writer = XmlWriter::new();
910 /// assert!(new_xml_writer.is_ok());
911 /// let start_amazon_effect_result = new_xml_writer.unwrap()
912 /// .start_ssml_vocal_tract_length("+10%".to_owned());
913 /// assert!(start_amazon_effect_result.is_ok());
914 /// ```
915 ///
916 /// Generated SSML:
917 ///
918 /// ```text
919 /// <?xml version="1.0"?>
920 /// <amazon:effect vocal-tract-length="+10%">
921 /// ```
922 pub fn start_ssml_vocal_tract_length(&mut self, factor: String) -> Result<()> {
923 let mut elem = BytesStart::owned(b"amazon:effect".to_vec(), "amazon:effect".len());
924 elem.push_attribute(("vocal-tract-length", &*format!("{}", factor)));
925 Ok(self.writer.write_event(Event::Start(elem))?)
926 }
927
928 /// Starts an SSML phonation tag. These tags are unique to AWS Polly. As such
929 /// the only place they are documented is inside the AWS Docs themsleves which are:
930 /// [HERE](http://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html).
931 ///
932 /// # Examples
933 ///
934 /// Rust Code:
935 ///
936 /// ```rust
937 /// use text_to_polly_ssml::xml_writer::XmlWriter;
938 /// use text_to_polly_ssml::ssml_constants::PhonationVolume;
939 /// let mut new_xml_writer = XmlWriter::new();
940 /// assert!(new_xml_writer.is_ok());
941 /// let start_amazon_phonation_result = new_xml_writer.unwrap()
942 /// .start_ssml_phonation(PhonationVolume::Soft);
943 /// assert!(start_amazon_phonation_result.is_ok());
944 /// ```
945 ///
946 /// Generated SSML:
947 ///
948 /// ```text
949 /// <?xml version="1.0"?>
950 /// <amazon:effect phonation="soft">
951 /// ```
952 pub fn start_ssml_phonation(&mut self, volume: PhonationVolume) -> Result<()> {
953 let mut elem = BytesStart::owned(b"amazon:effect".to_vec(), "amazon:effect".len());
954 elem.push_attribute(("phonation", &*format!("{}", volume)));
955 Ok(self.writer.write_event(Event::Start(elem))?)
956 }
957
958 /// Starts an SSML <amazon:auto-breaths> tag.
959 ///
960 /// # Examples
961 ///
962 /// Rust Code:
963 ///
964 /// ```rust
965 /// use text_to_polly_ssml::xml_writer::XmlWriter;
966 /// use text_to_polly_ssml::ssml_constants::*;
967 /// let mut new_xml_writer = XmlWriter::new();
968 /// assert!(new_xml_writer.is_ok());
969 /// let start_amazon_auto_breaths_result = new_xml_writer.unwrap().start_ssml_auto_breaths(
970 /// BreathVolumes::Def,
971 /// AutoBreathFrequency::Def,
972 /// BreathDuration::Def,
973 /// );
974 /// assert!(start_amazon_auto_breaths_result.is_ok());
975 /// ```
976 ///
977 /// Generated SSML:
978 ///
979 /// ```text
980 /// <?xml version="1.0"?>
981 /// <amazon:auto-breaths volume="default" frequency="default" duration="default">
982 /// ```
983 pub fn start_ssml_auto_breaths(
984 &mut self,
985 volume: BreathVolumes,
986 frequency: AutoBreathFrequency,
987 duration: BreathDuration,
988 ) -> Result<()> {
989 let mut elem =
990 BytesStart::owned(b"amazon:auto-breaths".to_vec(), "amazon:auto-breaths".len());
991 elem.push_attribute(("volume", &*format!("{}", volume)));
992 elem.push_attribute(("frequency", &*format!("{}", frequency)));
993 elem.push_attribute(("duration", &*format!("{}", duration)));
994 Ok(self.writer.write_event(Event::Start(elem))?)
995 }
996
997 /// Ends an SSML <amazon:auto-breaths> tag.
998 ///
999 /// # Examples
1000 ///
1001 /// Rust Code:
1002 ///
1003 /// ```rust
1004 /// use text_to_polly_ssml::xml_writer::XmlWriter;
1005 /// let mut new_xml_writer = XmlWriter::new();
1006 /// assert!(new_xml_writer.is_ok());
1007 /// let end_amazon_auto_breaths_result = new_xml_writer.unwrap().end_ssml_amazon_auto_breaths();
1008 /// assert!(end_amazon_auto_breaths_result.is_ok());
1009 /// ```
1010 ///
1011 /// Generated SSML:
1012 ///
1013 /// ```text
1014 /// <?xml version="1.0"?>
1015 /// </amazon:auto-breaths>
1016 /// ```
1017 pub fn end_ssml_amazon_auto_breaths(&mut self) -> Result<()> {
1018 Ok(self
1019 .writer
1020 .write_event(Event::End(BytesEnd::borrowed(b"amazon:auto-breaths")))?)
1021 }
1022
1023 /// Starts an SSML <amazon:breath> tag.
1024 ///
1025 /// # Examples
1026 ///
1027 /// Rust Code:
1028 ///
1029 /// ```rust
1030 /// use text_to_polly_ssml::xml_writer::XmlWriter;
1031 /// use text_to_polly_ssml::ssml_constants::*;
1032 /// let mut new_xml_writer = XmlWriter::new();
1033 /// assert!(new_xml_writer.is_ok());
1034 /// let start_amazon_breath_result = new_xml_writer.unwrap().write_amazon_breath(
1035 /// BreathVolumes::Def,
1036 /// BreathDuration::Def,
1037 /// );
1038 /// assert!(start_amazon_breath_result.is_ok());
1039 /// ```
1040 ///
1041 /// Generated SSML:
1042 ///
1043 /// ```text
1044 /// <?xml version="1.0"?>
1045 /// <amazon:breath volume="default" duration="default" />
1046 /// ```
1047 pub fn write_amazon_breath(
1048 &mut self,
1049 volume: BreathVolumes,
1050 duration: BreathDuration,
1051 ) -> Result<()> {
1052 let mut elem = BytesStart::owned(b"amazon:breath".to_vec(), "amazon:breath".len());
1053 elem.push_attribute(("volume", &*format!("{}", volume)));
1054 elem.push_attribute(("duration", &*format!("{}", duration)));
1055
1056 Ok(self.writer.write_event(Event::Empty(elem))?)
1057 }
1058
1059 /// Writes some raw text to the XML Document. Should only be used inbetween <p> tags.
1060 pub fn write_text(&mut self, text: &str) -> Result<()> {
1061 Ok(self
1062 .writer
1063 .write_event(Event::Text(BytesText::from_plain_str(text)))?)
1064 }
1065
1066 /// Renders the XML document in it's current state. This expects the document
1067 /// to be completely valid UTF-8, and will do no closing of tags for you.
1068 pub fn render(&mut self) -> String {
1069 String::from_utf8(self.writer.clone().into_inner().into_inner())
1070 .expect("SSML is not valid UTF-8!")
1071 }
1072}