Repository: typst/typst
File: crates/typst-cli/src/main.rs (Rust)

Code before:
use crate::args::{CliArguments, Command};
use crate::timings::Timer;

thread_local! {
    /// The CLI's exit code.
    static EXIT: Cell<ExitCode> = Cell::new(ExitCode::SUCCESS);
}

/// The parsed commandline arguments.
static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse);

/// Entry point.
fn main() -> ExitCode {
    let timer = Timer::new(&ARGS);

    let mut res = match &ARGS.command {
        Command::Compile(command) => crate::compile::compile(timer, command.clone()),
        Command::Watch(command) => crate::watch::watch(timer, command.clone()),
        Command::Query(command) => crate::query::query(command),
        Command::Fonts(command) => crate::fonts::fonts(command),
        Command::Update(command) => crate::update::update(command),
    };

    // Leave the alternate screen if it was opened. This operation is done here
    // so that it is executed prior to printing the final error.
    res = res.or(terminal::out()
        .leave_alternate_screen()
        .map_err(|err| eco_format!("failed to leave alternate screen ({err})")));

    if let Err(msg) = res {
        set_failed();
        print_error(&msg).expect("failed to print error");
    }

    EXIT.with(|cell| cell.get())
}

/// Ensure a failure exit code.
fn set_failed() {
    EXIT.with(|cell| cell.set(ExitCode::FAILURE));
}

/// Used by `args.rs`.
fn typst_version() -> &'static str {
    env!("TYPST_VERSION")
}

/// Print an application-level error (independent from a source file).
fn print_error(msg: &str) -> io::Result<()> {
    let styles = term::Styles::default();


Review comment (bug):
This or confused me for a bit. Perhaps, this should just be `let res2 = terminal::out()...` and then `if let Err(msg) = res.or(res2)`.

Or maybe we can use expect here. If we can't leave the alternative screen, will print_error not also most probably just fail and then we get "failed to print error" rather than the more helpful "failed to leave alternate screen" panic?

Code after:
use crate::args::{CliArguments, Command};
use crate::timings::Timer;

thread_local! {
    /// The CLI's exit code.
    static EXIT: Cell<ExitCode> = Cell::new(ExitCode::SUCCESS);
}

/// The parsed commandline arguments.
static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse);

/// Entry point.
fn main() -> ExitCode {
    let timer = Timer::new(&ARGS);

    let res = match &ARGS.command {
        Command::Compile(command) => crate::compile::compile(timer, command.clone()),
        Command::Watch(command) => crate::watch::watch(timer, command.clone()),
        Command::Query(command) => crate::query::query(command),
        Command::Fonts(command) => crate::fonts::fonts(command),
        Command::Update(command) => crate::update::update(command),
    };

    // Leave the alternate screen if it was opened. This operation is done here
    // so that it is executed prior to printing the final error.
    let res_leave = terminal::out()
        .leave_alternate_screen()
        .map_err(|err| eco_format!("failed to leave alternate screen ({err})"));

    if let Err(msg) = res.or(res_leave) {
        set_failed();
        print_error(&msg).expect("failed to print error");
    }

    EXIT.with(|cell| cell.get())
}

/// Ensure a failure exit code.
fn set_failed() {
    EXIT.with(|cell| cell.set(ExitCode::FAILURE));
}

/// Used by `args.rs`.
fn typst_version() -> &'static str {
    env!("TYPST_VERSION")
}

/// Print an application-level error (independent from a source file).
fn print_error(msg: &str) -> io::Result<()> {
    let styles = term::Styles::default();