typst-upgrade 1.7.0

A tool to upgrade typst packages
use std::fmt::Arguments;
use std::io::Write;
use std::sync::OnceLock;

use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

static COLOR_CHOICE: OnceLock<ColorChoice> = OnceLock::new();

pub fn init(color: clap::ColorChoice) {
    COLOR_CHOICE
        .set(match color {
            clap::ColorChoice::Auto => ColorChoice::Auto,
            clap::ColorChoice::Always => ColorChoice::Always,
            clap::ColorChoice::Never => ColorChoice::Never,
        })
        .unwrap();
}

pub fn color_choice() -> ColorChoice {
    *COLOR_CHOICE.get().unwrap_or(&ColorChoice::Auto)
}

#[cfg_attr(coverage_nightly, coverage(off))]
pub fn term_println(
    mut stream: StandardStream,
    color: Option<Color>,
    whole_line: bool,
    motion: &str,
    args: Arguments,
) -> Result<(), std::io::Error> {
    stream
        .set_color(ColorSpec::new().set_bold(true).set_fg(color))
        .and_then(|_| {
            write!(&mut stream, "{:>12} ", motion)?;
            if whole_line {
                stream.set_color(ColorSpec::new().set_reset(true).set_fg(color))
            } else {
                stream.set_color(ColorSpec::new().set_reset(true))
            }?;
            writeln!(&mut stream, "{}", args)
        })
}

macro_rules! __term_println {
    (@COLOR_MOTION $stream:ident, $color:expr, $motion:literal, $($args:tt)*) => {
        use std::io::IsTerminal;
        use termcolor::{ColorChoice, StandardStream};

        let stream = StandardStream::$stream(if std::io::$stream().is_terminal() {
            $crate::term::color_choice()
        } else {
            ColorChoice::Never
        });

        $crate::term::term_println(stream, $color, false, $motion, format_args!($($args)*))
            .expect(&format!("Cannot write to {}", stringify!($stream)));
    };

    (@COLOR_WHOLE_LINE $stream:ident, $color:expr, $motion:literal, $($args:tt)*) => {
        use std::io::IsTerminal;
        use termcolor::{ColorChoice, StandardStream};

        let stream = StandardStream::$stream(if std::io::$stream().is_terminal() {
            $crate::term::color_choice()
        } else {
            ColorChoice::Never
        });

        $crate::term::term_println(stream, $color, true, $motion, format_args!($($args)*))
            .expect(&format!("Cannot write to {}", stringify!($stream)));
    };
}

#[macro_export]
macro_rules! info {
    ($motion:literal: $($args:tt)*) => {
        __term_println!(@COLOR_MOTION stdout, Some(termcolor::Color::Green), $motion, $($args)*)
    };
    ($($args:tt)*) => {
        info!("INFO": $($args)*)
    };
}

#[macro_export]
macro_rules! warn {
    ($motion:literal: $($args:tt)*) => {
        __term_println!(@COLOR_MOTION stderr, Some(termcolor::Color::Yellow), $motion, $($args)*)
    };
    ($($args:tt)*) => {
        warn!("WARN": $($args)*)
    };
}

#[macro_export]
macro_rules! error {
    ($motion:literal: $($args:tt)*) => {
        __term_println!(@COLOR_MOTION stderr, Some(termcolor::Color::Red), $motion, $($args)*)
    };
    ($($args:tt)*) => {
        error!("ERROR": $($args)*)
    };
}

#[macro_export]
macro_rules! diff {
    (del $($args:tt)*) => {
        __term_println!(@COLOR_WHOLE_LINE stdout, Some(termcolor::Color::Red), "-", $($args)*)
    };
    (add $($args:tt)*) => {
        __term_println!(@COLOR_WHOLE_LINE stdout, Some(termcolor::Color::Green), "+", $($args)*)
    };
    ($($args:tt)*) => {
        __term_println!(@COLOR_WHOLE_LINE stdout, None, "", $($args)*)
    };
}

#[cfg(test)]
mod test {
    use termcolor::ColorChoice;

    #[test]
    fn init() {
        super::init(clap::ColorChoice::Auto);
        assert_eq!(super::color_choice(), ColorChoice::Auto);

        assert!(
            std::panic::catch_unwind(|| {
                super::init(clap::ColorChoice::Always);
            })
            .is_err()
        );

        assert!(
            std::panic::catch_unwind(|| {
                super::init(clap::ColorChoice::Never);
            })
            .is_err()
        );
    }
}