quinjet 0.0.49

A fast, live, keyboard-first Git source-control interface for the terminal
#[cfg_attr(not(test), expect(clippy::wildcard_imports, reason = "shared"))]
use super::*;

pub(super) struct Emitter {
    pub(super) json: bool,
    pub(super) progress: Option<ProgressBar>,
}

impl Emitter {
    pub(super) const fn new(json: bool) -> Self {
        Self {
            json,
            progress: None,
        }
    }

    pub(super) fn start_progress(&mut self, label: &'static str) -> Result<()> {
        if !progress_enabled(self.json, io::stderr().is_terminal()) {
            return Ok(());
        }
        let progress = progress_bar(label, ProgressDrawTarget::stderr())?;
        progress.enable_steady_tick(Duration::from_millis(100));
        self.progress = Some(progress);
        Ok(())
    }

    pub(super) fn set_progress(&self, label: &'static str) {
        if let Some(progress) = &self.progress {
            progress.set_message(label);
        }
    }

    pub(super) fn note(&self, text: &str) {
        if let Some(progress) = &self.progress {
            progress.println(text);
        } else {
            note(text);
        }
    }

    pub(super) fn finish_progress(&self) {
        if let Some(progress) = &self.progress {
            progress.finish_and_clear();
        }
    }

    pub(super) fn execute(&self, session: &mut Session, command: Command) -> Result<Outcome> {
        self.set_progress(command.progress_label());
        session.execute_with(
            command,
            &mut |event| self.set_progress(event.label()),
            &|| true,
        )
    }

    pub(super) fn emit<T: Serialize>(
        &self,
        value: &T,
        text: impl FnOnce() -> String,
    ) -> Result<()> {
        self.finish_progress();
        let mut stdout = io::stdout().lock();
        if self.json {
            writeln!(stdout, "{}", serde_json::to_string_pretty(value)?)?;
        } else {
            write!(stdout, "{}", text())?;
        }
        stdout.flush()?;
        Ok(())
    }

    pub(super) fn message(&self, message: &str) -> Result<()> {
        self.emit(&Message { message }, || format!("{message}\n"))
    }
}

pub(super) const fn progress_enabled(json: bool, stderr_terminal: bool) -> bool {
    !json && stderr_terminal
}

pub(super) fn progress_bar(label: &'static str, target: ProgressDrawTarget) -> Result<ProgressBar> {
    let progress = ProgressBar::with_draw_target(None, target);
    progress.set_style(
        ProgressStyle::with_template("{spinner:.cyan} {msg}")?.tick_strings(&["-", "\\", "|", "/"]),
    );
    progress.set_message(label);
    Ok(progress)
}

#[derive(Serialize)]
pub(super) struct Message<'a> {
    pub(super) message: &'a str,
}