texlang_stdlib/
errormode.rs

1//! Commands that control what to do when errors occur
2//!
3//! The commands in this module have ownership both over the recoverable error
4//! handler and the input terminal.
5
6use std::cell::RefCell;
7use std::rc::Rc;
8use texlang::traits::*;
9use texlang::*;
10use texlang_common as common;
11
12#[derive(Default)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Component {
15    mode: Mode,
16    #[cfg_attr(feature = "serde", serde(skip))]
17    default_terminal: DefaultTerminal,
18    errors: RefCell<Vec<error::TracedTexError>>,
19}
20
21impl Component {
22    /// Set the default terminal.
23    pub fn set_default_terminal(&mut self, default_terminal: Rc<RefCell<dyn common::TerminalIn>>) {
24        self.default_terminal = DefaultTerminal(default_terminal);
25    }
26}
27
28/// Wrapper type so we can implement the default trait.
29struct DefaultTerminal(Rc<RefCell<dyn common::TerminalIn>>);
30
31impl Default for DefaultTerminal {
32    fn default() -> Self {
33        Self(Rc::new(RefCell::new(std::io::stdin())))
34    }
35}
36
37impl common::HasTerminalIn for Component {
38    fn terminal_in(&self) -> Rc<RefCell<dyn common::TerminalIn>> {
39        match self.mode {
40            Mode::ErrorStop | Mode::Scroll => self.default_terminal.0.clone(),
41            Mode::Batch | Mode::NonStop => Rc::new(RefCell::new(DisabledTerminalIn {})),
42        }
43    }
44}
45
46#[derive(Default, PartialEq, Eq, Debug)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48enum Mode {
49    #[default]
50    ErrorStop,
51    Scroll,
52    NonStop,
53    Batch,
54}
55
56/// Get the `\errorstopmode` command.
57pub fn get_errorstopmode<S: HasComponent<Component>>() -> command::BuiltIn<S> {
58    command::BuiltIn::new_execution(|_, input: &mut vm::ExecutionInput<S>| {
59        set_mode(input, Mode::ErrorStop);
60        Ok(())
61    })
62}
63
64/// Get the `\scrollmode` command.
65pub fn get_scrollmode<S: HasComponent<Component>>() -> command::BuiltIn<S> {
66    command::BuiltIn::new_execution(|_, input: &mut vm::ExecutionInput<S>| {
67        set_mode(input, Mode::Scroll);
68        Ok(())
69    })
70}
71
72/// Get the `\nonstopmode` command.
73pub fn get_nonstopmode<S: HasComponent<Component>>() -> command::BuiltIn<S> {
74    command::BuiltIn::new_execution(|_, input: &mut vm::ExecutionInput<S>| {
75        set_mode(input, Mode::NonStop);
76        Ok(())
77    })
78}
79
80/// Get the `\batchmode` command.
81pub fn get_batchmode<S: HasComponent<Component>>() -> command::BuiltIn<S> {
82    command::BuiltIn::new_execution(|_, input: &mut vm::ExecutionInput<S>| {
83        set_mode(input, Mode::Batch);
84        Ok(())
85    })
86}
87
88pub fn recoverable_error_hook<S: HasComponent<Component> + common::HasLogging>(
89    state: &S,
90    recoverable_error: error::TracedTexError,
91) -> Result<(), Box<dyn error::TexError>> {
92    match &state.component().mode {
93        Mode::ErrorStop => {
94            return Err(recoverable_error.error);
95        }
96        Mode::Scroll | Mode::NonStop => {
97            writeln!(state.terminal_out().borrow_mut(), "{recoverable_error}").unwrap();
98        }
99        Mode::Batch => {}
100    }
101    writeln!(state.log_file().borrow_mut(), "{recoverable_error}").unwrap();
102    state
103        .component()
104        .errors
105        .borrow_mut()
106        .push(recoverable_error);
107    Ok(())
108}
109
110fn set_mode<S: HasComponent<Component>>(input: &mut vm::ExecutionInput<S>, mode: Mode) {
111    input.state_mut().component_mut().mode = mode;
112}
113
114struct DisabledTerminalIn;
115
116impl common::TerminalIn for DisabledTerminalIn {
117    fn read_line(&mut self, _: Option<&str>, _: &mut String) -> std::io::Result<()> {
118        todo!()
119    }
120}