## 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
    }