Coverage Report

Created: 2026-05-25 08:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/runner/work/MathCAT/MathCAT/src/bin/mathml2text.rs
Line
Count
Source
1
// *** MathCAT doesn't normally want to build a binary ***
2
// *** This file is here because it is useful for trying out things ***
3
#![allow(clippy::needless_return)]
4
5
use libmathcat::{errors::*, interface::*};
6
use log::*;
7
use std::path::PathBuf;
8
use clap::{Parser, ValueEnum};
9
10
// Maybe also have this speak to test the TTS generation.
11
// There is a rust winapi crate that mirrors the WinPAI and has "Speak(...)" in it
12
13
// env RUST_LOG=DEBUG cargo run --features "include-zip"
14
0
fn get_rules_dir() -> String {
15
    // for testing with zipped rules dir
16
    // let rules_path = std::env::current_exe().unwrap().parent().unwrap().join("../../../MathCATForPython/addon/globalPlugins/MathCAT/Rules");
17
0
    let rules_path = std::env::current_exe().unwrap().parent().unwrap().join("../../Rules");
18
0
    return rules_path.as_os_str().to_str().unwrap().to_string();
19
0
}
20
21
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
22
enum OutputType {
23
    Text,
24
    Braille,
25
    #[cfg(feature="tts")]
26
    Speech,
27
}
28
29
#[derive(Parser)]
30
#[command(version, about)]
31
struct Options {
32
    #[arg(short, long)]
33
    rules_dir: Option<PathBuf>,
34
35
    input_file: Option<PathBuf>,
36
37
    #[arg(short, long, default_value="en")]
38
    language: String,
39
40
    #[arg(value_enum, long, default_value="text")]
41
    output: OutputType,
42
}
43
44
45
0
fn main() -> Result<()> {
46
0
    env_logger::builder()
47
0
      .format_timestamp(None)
48
0
      .format_module_path(false)
49
0
      .format_indent(Some(2))
50
0
      .format_level(false)
51
0
      .init();
52
53
0
    let cli = Options::parse();
54
55
0
    let expr = if let Some(f) = cli.input_file {
56
0
  std::fs::read_to_string(&f).with_context(|| format!("unable to open {}", f.to_str().unwrap_or_default()))?
57
    } else {
58
0
        r#"
59
0
            <math xmlns="http://www.w3.org/1998/Math/MathML"><mo>(</mo><mn>1</mn><mo>)</mo></math>
60
0
    "#.to_string()
61
    };
62
63
0
    if let Err(e) = set_rules_dir(get_rules_dir()) {
64
0
  panic!("Error: exiting -- {}", errors_to_string(&e));
65
0
    }
66
0
    debug!("Languages: {}", libmathcat::interface::get_supported_languages()?.join(", "));
67
68
    #[cfg(feature = "include-zip")]
69
    info!("***********include-zip is present**********");
70
0
    info!("Version = '{}' using Rules dir {}", get_version(), get_rules_dir());
71
0
    set_preference("Language", cli.language)?;
72
73
0
    set_preference("DecimalSeparator", "Auto").unwrap();
74
0
    set_preference("BrailleCode", "Nemeth").unwrap();
75
0
    set_preference("TTS", "None").unwrap();
76
0
    set_preference("Verbosity", "Verbose").unwrap();
77
0
    set_preference("NavVerbosity", "Verbose").unwrap();
78
0
    set_preference("NavMode", "Enhanced").unwrap();
79
0
    set_preference("Impairment", "Blindness").unwrap();
80
0
    set_preference("SpeechOverrides_CapitalLetters", "").unwrap();
81
0
    set_preference("MathRate", "80").unwrap();
82
0
    set_preference("CapitalLetters_Beep", "true").unwrap();
83
0
    set_preference("IntentErrorRecovery", "Error").unwrap();
84
85
0
    set_preference("Bookmark", "false").unwrap();
86
0
    set_preference("SpeechStyle", "ClearSpeak").unwrap();
87
0
    info!("Languages: {}", libmathcat::interface::get_supported_languages()?.join(", "));
88
0
    info!("Speech styles: {}", libmathcat::interface::get_supported_speech_styles("ClearSpeak")?.join(", "));
89
0
    info!("BrailleCodes: {}", libmathcat::interface::get_supported_braille_codes()?.join(", "));
90
91
0
    debug!("Speech language is {}", get_preference("Language").unwrap());
92
0
    debug!("DecimalSeparator: {:?}", get_preference("DecimalSeparator").unwrap());
93
0
    debug!("DecimalSeparators: {:?}, BlockSeparators: {:?}", get_preference("DecimalSeparators").unwrap(), get_preference("BlockSeparators").unwrap());
94
0
    debug!("SpeechStyle: {:?}", get_preference("SpeechStyle").unwrap());
95
0
    debug!("Verbosity: {:?}", get_preference("Verbosity").unwrap());
96
97
0
    match set_mathml(&expr) {
98
0
  Err(e) => {
99
0
      panic!("Error: exiting -- {}", errors_to_string(&e));
100
  },
101
0
  Ok(fmt) => {
102
0
      info!("formatted input mathml into {fmt}");
103
  }
104
    }
105
106
0
    match cli.output {
107
  OutputType::Text => {
108
0
      match get_spoken_text() {
109
0
    Ok(speech) => println!("{speech}"),
110
0
    Err(e) => panic!("{}", errors_to_string(&e)),
111
      }
112
  },
113
  OutputType::Braille => {
114
0
      debug!("...using BrailleCode: {:?}", get_preference("BrailleCode").unwrap());
115
0
      match get_braille("") {
116
0
    Ok(braille) => println!("{braille}"),
117
0
    Err(e) => panic!("{}", errors_to_string(&e)),
118
      }
119
  },
120
  #[cfg(feature="tts")]
121
  OutputType::Speech => {
122
      // Create the NaturalTts struct using the builder pattern.
123
      let mut natural = natural_tts::NaturalTtsBuilder::default()
124
    .gtts_model(natural_tts::models::gtts::GttsModel::default())
125
    .default_model(natural_tts::Model::Gtts)
126
    .build().expect("failed to generate natural tts gtts model");
127
128
129
      // Start producing an output using the default_model.
130
      let _ = natural.start(get_spoken_text().unwrap(), &PathBuf::from("output.wav"));
131
132
      // Play the audio until it finishes
133
      natural.sleep_until_end();
134
  }
135
    }
136
137
0
    Ok(())
138
0
}