use httpgenerator_cli::{AzureAuthStatus, CliError, ExecutionObserver};
use httpgenerator_core::openapi::OpenApiInspection;
use std::{
io::{self, IsTerminal, Write as IoWrite},
path::PathBuf,
time::Duration,
};
use super::{
format::{PresentationMode, mode_from_terminal},
render::{
render_azure_auth_finished, render_azure_auth_started, render_error,
render_file_writing_started, render_files_written, render_header, render_success,
render_validation_started, render_validation_succeeded,
},
};
pub(crate) struct CliPresenter {
stdout_mode: PresentationMode,
stderr_mode: PresentationMode,
}
impl CliPresenter {
pub(crate) fn detect() -> Self {
Self::new(
mode_from_terminal(io::stdout().is_terminal()),
mode_from_terminal(io::stderr().is_terminal()),
)
}
fn new(stdout_mode: PresentationMode, stderr_mode: PresentationMode) -> Self {
Self {
stdout_mode,
stderr_mode,
}
}
pub(crate) fn print_header(&mut self, no_logging: bool) {
self.write_stdout(&render_header(self.stdout_mode, no_logging));
}
pub(crate) fn print_success(&mut self, duration: Duration) {
self.write_stdout(&render_success(self.stdout_mode, duration));
}
pub(crate) fn print_error(&mut self, error: &CliError) {
self.write_stderr(&render_error(self.stderr_mode, error));
}
fn write_stdout(&self, output: &str) {
if output.is_empty() {
return;
}
print!("{output}");
let _ = io::stdout().flush();
}
fn write_stderr(&self, output: &str) {
if output.is_empty() {
return;
}
eprint!("{output}");
let _ = io::stderr().flush();
}
}
impl ExecutionObserver for CliPresenter {
fn validation_started(&mut self) {
self.write_stdout(&render_validation_started(self.stdout_mode));
}
fn validation_succeeded(&mut self, inspection: &OpenApiInspection) {
self.write_stdout(&render_validation_succeeded(self.stdout_mode, inspection));
}
fn azure_auth_started(&mut self) {
self.write_stdout(&render_azure_auth_started(self.stdout_mode));
}
fn azure_auth_finished(&mut self, status: &AzureAuthStatus) {
match status {
AzureAuthStatus::Failed { .. } => self.write_stderr(&render_azure_auth_finished(
self.stdout_mode,
self.stderr_mode,
status,
)),
_ => self.write_stdout(&render_azure_auth_finished(
self.stdout_mode,
self.stderr_mode,
status,
)),
}
}
fn file_writing_started(&mut self, file_count: usize) {
self.write_stdout(&render_file_writing_started(self.stdout_mode, file_count));
}
fn files_written(&mut self, paths: &[PathBuf]) {
self.write_stdout(&render_files_written(self.stdout_mode, paths));
}
}
#[cfg(test)]
mod tests {
use crate::ui::{format::PresentationMode, render::render_plain_header};
use super::CliPresenter;
#[test]
fn plain_header_stays_semantic_without_rich_markers() {
let header = render_plain_header(true);
assert!(header.contains("HTTP File Generator v"));
assert!(header.contains("Support key: Unavailable when logging is disabled"));
assert!(!header.contains("╭"));
assert!(!header.contains("🚀"));
}
#[test]
fn rich_header_uses_a_panel_layout() {
let presenter = CliPresenter::new(PresentationMode::Rich, PresentationMode::Rich);
let mut header = String::new();
header.push_str(&super::render_header(presenter.stdout_mode, true));
assert!(header.contains("╭"));
assert!(header.contains("🚀 HTTP File Generator"));
assert!(header.contains("⚠️ Unavailable when logging is disabled"));
}
}