| 787 | } |
| 788 | |
| 789 | pub fn replace_array_string<'c, 's:'c, 'm:'c>(&self, rules_with_context: &'r mut SpeechRulesWithContext<'c, 's,'m>, mathml: Element<'c>) -> Result<String> { |
| 790 | // loop over the replacements and build up a vector of strings, excluding empty ones. |
| 791 | // * eliminate any redundance |
| 792 | // * add/replace auto-pauses |
| 793 | // * join the remaining vector together |
| 794 | let mut replacement_strings = Vec::with_capacity(self.replacements.len()); // probably conservative guess |
| 795 | for replacement in self.replacements.iter() { |
| 796 | let string: String = rules_with_context.replace(replacement, mathml)?; |
| 797 | if !string.is_empty() { |
| 798 | replacement_strings.push(string); |
| 799 | } |
| 800 | } |
| 801 | |
| 802 | if replacement_strings.is_empty() { |
| 803 | return Ok( "".to_string() ); |
| 804 | } |
| 805 | // delete an optional text that is repetitive |
| 806 | // we do this by looking for the optional text marker, and if present, check for repetition at end of previous string |
| 807 | // if repetitive, we delete the optional string |
| 808 | // if not, we leave the markers because the repetition might happen several "levels" up |
| 809 | // this could also be done in a final cleanup of the entire string (where we remove any markers), |
| 810 | // but the match is harder (rust regex lacks look behind pattern match) and it is less efficient |
| 811 | // Note: we skip the first string since it can't be repetitive of something at this level |
| 812 | for i in 1..replacement_strings.len()-1 { |
| 813 | if let Some(bytes) = is_repetitive(&replacement_strings[i-1], &replacement_strings[i]) { |
| 814 | replacement_strings[i] = bytes.to_string(); |
| 815 | } |
| 816 | } |
| 817 | |
| 818 | for i in 0..replacement_strings.len() { |
| 819 | if replacement_strings[i].contains(PAUSE_AUTO_STR) { |
| 820 | let before = if i == 0 {""} else {&replacement_strings[i-1]}; |
| 821 | let after = if i+1 == replacement_strings.len() {""} else {&replacement_strings[i+1]}; |
| 822 | replacement_strings[i] = replacement_strings[i].replace( |
| 823 | PAUSE_AUTO_STR, |
| 824 | &rules_with_context.speech_rules.pref_manager.borrow().get_tts().compute_auto_pause(&rules_with_context.speech_rules.pref_manager.borrow(), before, after)); |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | // join the strings together with spaces in between |
| 829 | // concatenation (removal of spaces) is saved for the top level because they otherwise are stripped at the wrong sometimes |
| 830 | return Ok( replacement_strings.join(" ") ); |
| 831 | |
| 832 | fn is_repetitive<'a>(prev: &str, optional: &'a str) -> Option<&'a str> { |
| 833 | // OPTIONAL_INDICATOR surrounds the optional text |
| 834 | // minor optimization -- lots of short strings and the OPTIONAL_INDICATOR takes a few bytes, so skip the check for those strings |
| 835 | if optional.len() <= 2 * OPTIONAL_INDICATOR_LEN { |
| 836 | return None; |
| 837 | } |
| 838 | |
| 839 | // should be exactly one match -- ignore more than one for now |
| 840 | match optional.find(OPTIONAL_INDICATOR) { |
| 841 | None => return None, |
| 842 | Some(start_index) => { |
| 843 | let optional_word_start_slice = &optional[start_index + OPTIONAL_INDICATOR_LEN..]; |
| 844 | // now find the end |
| 845 | match optional_word_start_slice.find(OPTIONAL_INDICATOR) { |
| 846 | None => panic!("Internal error: missing end optional char -- text handling is corrupted!"), |