Skip to content

Commit

Permalink
Add a preference CheckRuleFiles with values All, Prefs, and `N…
Browse files Browse the repository at this point in the history
…one` to tell MathCAT whether it should check the Rule files on every call (set_mathml, get speech/braille, or navigate) to see if they have changed.

Fixes #249
  • Loading branch information
NSoiffer committed Feb 7, 2024
1 parent eaf41c7 commit 479903d
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 53 deletions.
70 changes: 46 additions & 24 deletions src/prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ impl Preferences{
prefs.insert("CapitalLetters_Pitch".to_string(), Yaml::Real("0.0".to_string()));
prefs.insert("CapitalLetters_Beep".to_string(), Yaml::Boolean(false));
prefs.insert("IntentErrorRecovery".to_string(), Yaml::String("IgnoreIntent".to_string())); // also Error
prefs.insert("CheckRuleFiles".to_string(), Yaml::String("All".to_string())); // avoid checking for rule files being changed (40% speedup!) (All, Prefs, None)
return Preferences{ prefs };
}

Expand Down Expand Up @@ -249,7 +250,7 @@ impl PreferenceManager {
/// If rules_dir is an empty PathBuf, the existing rules_dir is used (an error if it doesn't exist)
pub fn initialize(&mut self, rules_dir: PathBuf) -> Result<()> {
self.set_rules_dir(&rules_dir)?;
self.set_preferences()?;
self.set_preference_files()?;
self.set_all_files(&rules_dir)?;
return Ok( () );
}
Expand Down Expand Up @@ -282,7 +283,7 @@ impl PreferenceManager {

/// Read the preferences from the files (if not up to date) and set the preferences and preference files
/// Returns failure if the files don't exist or have errors
fn set_preferences(&mut self) -> Result<()> {
pub fn set_preference_files(&mut self) -> Result<()> {
// first, read in the preferences -- need to determine which files to read next
// the prefs files are in the rules dir and the user dir; differs from other files
if self.api_prefs.prefs.is_empty() {
Expand Down Expand Up @@ -325,6 +326,7 @@ impl PreferenceManager {
};
bail!("Didn't find preferences in rule directory ('{}') or user directory ('{}')", &system_prefs_file.to_string_lossy(), user_prefs_file_name);
}
self.set_based_on_changes(&prefs)?;
self.user_prefs = prefs;
return Ok( () );
}
Expand Down Expand Up @@ -378,6 +380,36 @@ impl PreferenceManager {
return Ok( () );
}

/// If some preferences have changed, we may need to recompute other ones
/// The key prefs are Language, SpeechStyle, and BrailleCode
fn set_based_on_changes(&mut self, new_prefs: &Preferences) -> Result<()> {
let old_language = self.user_prefs.prefs.get("Language"); // not set if first time
if old_language.is_none() {
return Ok( () ); // if "Language" isn't set yet, nothing else is either -- first time through, so no updating needed.
}
let old_language = old_language.unwrap();
let new_language = new_prefs.prefs.get("Language").unwrap();
if old_language != new_language {
let language_dir = self.rules_dir.to_path_buf().join("Languages");
self.set_speech_files(&language_dir, new_language.as_str().unwrap())?; // also sets style file
} else {
let old_speech_style = self.user_prefs.prefs.get("SpeechStyle").unwrap();
let new_speech_style = new_prefs.prefs.get("SpeechStyle").unwrap();
let language_dir = self.rules_dir.to_path_buf().join("Languages");
if old_speech_style != new_speech_style {
self.set_speech_files(&language_dir, new_speech_style.as_str().unwrap())?;
}
}

let old_braille_code = self.user_prefs.prefs.get("BrailleCode").unwrap();
let new_braille_code = new_prefs.prefs.get("BrailleCode").unwrap();
if old_braille_code != new_braille_code {
let braille_code_dir = self.rules_dir.to_path_buf().join("Braille");
self.set_braille_files(&braille_code_dir, new_braille_code.as_str().unwrap())?; // also sets style file
}

return Ok( () );
}

/// Find a file matching `file_name` by starting in the regional directory and looking to the language.
/// If that fails, fall back to looking for the default repeating the same process -- something needs to be found or MathCAT crashes
Expand Down Expand Up @@ -549,24 +581,29 @@ impl PreferenceManager {
panic!("Internal error: set_api_string_pref called on invalid PreferenceManager -- error message\n{}", &self.error);
};

self.reset_preferences(key, value)?;
self.api_prefs.prefs.insert(key.to_string(), Yaml::String(value.to_string()));

return Ok( () );
}

fn reset_preferences(&mut self, changed_pref: &str, changed_value: &str) -> Result<()> {
let language_dir = self.rules_dir.to_path_buf().join("Languages");
match key {
match changed_pref {
"Language" => {
self.set_speech_files(&language_dir, value)?
self.set_speech_files(&language_dir, changed_value)?
},
"SpeechStyle" => {
let language = self.pref_to_string("Language");
let language = if language.as_str() == "Auto" {"en"} else {language.as_str()}; // avoid 'temp value dropped while borrowed' error
self.set_style_file(&language_dir, language, value)?
self.set_style_file(&language_dir, language, changed_value)?
},
"BrailleCode" => {
let braille_dir = self.rules_dir.to_path_buf().join("Braille");
self.set_braille_files(&braille_dir, value)?
self.set_braille_files(&braille_dir, changed_value)?
},
_ => (),
}
self.api_prefs.prefs.insert(key.to_string(), Yaml::String(value.to_string()));

return Ok( () );
}

Expand Down Expand Up @@ -640,22 +677,7 @@ impl PreferenceManager {
panic!("Internal error: set_user_prefs called on invalid PreferenceManager -- error message\n{}", &self.error);
};

let language_dir = self.rules_dir.to_path_buf().join("Languages");
match key {
"Language" => {
self.set_speech_files(&language_dir, value)?;
},
"SpeechStyle" => {
let language = self.pref_to_string("Language");
let language = if language.as_str() == "Auto" {"en"} else {language.as_str()}; // avoid 'temp value dropped while borrowed' error
self.set_style_file(&language_dir, language, value)?;
},
"BrailleCode" => {
let braille_dir = self.rules_dir.to_path_buf().join("Braille");
self.set_braille_files(&braille_dir, value)?;
},
_ => (),
}
self.reset_preferences(key, value)?;
self.user_prefs.set_string_value(key, value);
return Ok(());
}
Expand Down
73 changes: 44 additions & 29 deletions src/speech.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1916,15 +1916,16 @@ impl FilesAndTimes {
}

/// Returns true if the main file matches the corresponding preference location and files' times are all current
pub fn is_file_up_to_date(&self, pref_path: &Path) -> bool {
if cfg!(target_family = "wasm") {
return !self.ft.is_empty();
}
pub fn is_file_up_to_date(&self, pref_path: &Path, should_ignore_file_time: bool) -> bool {

// if the time isn't set or the path is different from the prefernce (which might have changed), return false
if self.ft.is_empty() || self.ft[0].time == SystemTime::UNIX_EPOCH || self.as_path() != pref_path {
return false;
}
if should_ignore_file_time || cfg!(target_family = "wasm") {
return !self.ft.is_empty();
}


// check the time stamp on the included files -- if the head file hasn't changed, the the paths for the included files will the same
for file in &self.ft {
Expand Down Expand Up @@ -2105,8 +2106,13 @@ impl SpeechRules {
}

pub fn read_files(&mut self) -> Result<()> {
let check_rule_files = self.pref_manager.borrow().pref_to_string("CheckRuleFiles");
if check_rule_files != "None" { // "Prefs" or "All" are other values
self.pref_manager.borrow_mut().set_preference_files()?;
}
let should_ignore_file_time = self.pref_manager.borrow().pref_to_string("CheckRuleFiles") != "All"; // ignore for "None", "Prefs"
let rule_file = self.pref_manager.borrow().get_rule_file(&self.name).to_path_buf(); // need to create PathBuf to avoid a move/use problem
if self.rules.is_empty() || !self.rule_files.is_file_up_to_date(&rule_file) {
if self.rules.is_empty() || !self.rule_files.is_file_up_to_date(&rule_file, should_ignore_file_time) {
self.rules.clear();
let files_read = self.read_patterns(&rule_file)?;
self.rule_files.set_files_and_times(files_read);
Expand All @@ -2115,12 +2121,15 @@ impl SpeechRules {
let pref_manager = self.pref_manager.borrow();
let unicode_pref_files = if self.name == RulesFor::Braille {pref_manager.get_braille_unicode_file()} else {pref_manager.get_speech_unicode_file()};

if !self.unicode_short_files.borrow().is_file_up_to_date(unicode_pref_files.0) {
if !self.unicode_short_files.borrow().is_file_up_to_date(unicode_pref_files.0, should_ignore_file_time) {
self.unicode_short.borrow_mut().clear();
self.unicode_short_files.borrow_mut().set_files_and_times(self.read_unicode(None, true)?);
}

if !self.definitions_files.borrow().is_file_up_to_date(pref_manager.get_definitions_file(self.name != RulesFor::Braille)) {
if self.definitions_files.borrow().ft.is_empty() || !self.definitions_files.borrow().is_file_up_to_date(
pref_manager.get_definitions_file(self.name != RulesFor::Braille),
should_ignore_file_time
) {
self.definitions_files.borrow_mut().set_files_and_times(read_definitions_file(self.name != RulesFor::Braille)?);
}
return Ok( () );
Expand Down Expand Up @@ -2545,8 +2554,9 @@ impl<'c, 's:'c, 'r, 'm:'c> SpeechRulesWithContext<'c, 's,'m> {
if replacements.is_none() {
// see if it in the full unicode table (if it isn't loaded already)
let pref_manager = rules.pref_manager.borrow();
let unicode_pref_files = if rules.name == RulesFor::Braille {pref_manager.get_braille_unicode_file()} else {pref_manager.get_speech_unicode_file()};
if rules.unicode_full.borrow().is_empty() || !rules.unicode_full_files.borrow().is_file_up_to_date(unicode_pref_files.1) {
let unicode_pref_files = if rules.name == RulesFor::Braille {pref_manager.get_braille_unicode_file()} else {pref_manager.get_speech_unicode_file()};
let should_ignore_file_time = pref_manager.pref_to_string("CheckRuleFiles") == "All";
if rules.unicode_full.borrow().is_empty() || !rules.unicode_full_files.borrow().is_file_up_to_date(unicode_pref_files.1, should_ignore_file_time) {
info!("*** Loading full unicode {} for char '{}'/{:#06x}", rules.name, ch, ch_as_u32);
rules.unicode_full.borrow_mut().clear();
rules.unicode_full_files.borrow_mut().set_files_and_times(rules.read_unicode(None, false)?);
Expand Down Expand Up @@ -2703,9 +2713,6 @@ mod tests {
#[test]
fn test_up_to_date() {
use crate::interface::*;
use std::fs;
use std::thread::sleep;
use std::time::Duration;
// initialize and move to a directory where making a time change doesn't really matter
set_rules_dir(super::super::abs_rules_dir_path()).unwrap();
set_preference("Language".to_string(), "zz-aa".to_string()).unwrap();
Expand All @@ -2715,25 +2722,33 @@ mod tests {
panic!("Should not be an error in setting MathML")
}

// files are read in due to setting the MathML -- record the time
SPEECH_RULES.with(|rules| {
let start_main_file = rules.borrow().unicode_short_files.borrow().ft[0].clone();
set_preference("CheckRuleFiles".to_string(), "All".to_string()).unwrap();
assert!(is_file_time_same(), "file's time did not get updated");
set_preference("CheckRuleFiles".to_string(), "None".to_string()).unwrap();
assert!(!is_file_time_same(), "file's time was wrongly updated (preference 'CheckRuleFiles' should have prevented updating)");

// open the file, read all the contents, then write them back so the time changes
let contents = fs::read(&start_main_file.file).expect(&format!("Failed to read file {} during test", &start_main_file.file.to_string_lossy()));
#[allow(unused_must_use)] {
fs::write(start_main_file.file, contents);
sleep(Duration::from_millis(10));
}
// change a file, cause read_files to be called, and return if MathCAT noticed the change and updated its time
fn is_file_time_same() -> bool {
// read and write a unicode file in a test dir
// files are read in due to setting the MathML

// speak should cause the file stored to have a new time
if let Err(e) = get_spoken_text() {
error!("{}", crate::errors_to_string(&e));
panic!("Should not be an error in speech")
}
let updated_main_file = rules.borrow().unicode_short_files.borrow().ft[0].clone();
assert!(start_main_file.time < updated_main_file.time);
});
use std::time::Duration;
return SPEECH_RULES.with(|rules| {
let start_main_file = rules.borrow().unicode_short_files.borrow().ft[0].clone();

// open the file, read all the contents, then write them back so the time changes
let contents = std::fs::read(&start_main_file.file).expect(&format!("Failed to read file {} during test", &start_main_file.file.to_string_lossy()));
std::fs::write(start_main_file.file, contents).unwrap();
std::thread::sleep(Duration::from_millis(5)); // pause a little to make sure the time changes

// speak should cause the file stored to have a new time
if let Err(e) = get_spoken_text() {
error!("{}", crate::errors_to_string(&e));
panic!("Should not be an error in speech")
}
return rules.borrow().unicode_short_files.borrow().ft[0].time == start_main_file.time;
});
}
}

// #[test]
Expand Down

0 comments on commit 479903d

Please sign in to comment.