#![forbid(unsafe_code)]
use crate::json_wire::{self, ErrorEnvelope, SuccessEnvelope};
use std::fmt;
use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
static QUIET: AtomicBool = AtomicBool::new(false);
static JSON_ERRORS: AtomicBool = AtomicBool::new(false);
pub fn set_quiet(quiet: bool) {
QUIET.store(quiet, Ordering::Relaxed);
}
pub fn set_json_errors(json: bool) {
JSON_ERRORS.store(json, Ordering::Relaxed);
}
#[must_use]
pub fn is_quiet() -> bool {
QUIET.load(Ordering::Relaxed)
}
#[must_use]
pub fn wants_json_errors() -> bool {
JSON_ERRORS.load(Ordering::Relaxed)
}
pub fn write_line_to(out: &mut impl Write, content: &str) -> io::Result<()> {
out.write_all(content.as_bytes())?;
out.write_all(b"\n")?;
out.flush()?;
Ok(())
}
pub fn write_line_to_fmt(out: &mut impl Write, args: fmt::Arguments<'_>) -> io::Result<()> {
out.write_fmt(args)?;
out.write_all(b"\n")?;
out.flush()?;
Ok(())
}
pub fn write_line(content: &str) -> io::Result<()> {
let stdout = io::stdout();
let mut handle = stdout.lock();
write_line_to(&mut handle, content)
}
pub fn write_line_fmt(args: fmt::Arguments<'_>) -> io::Result<()> {
let stdout = io::stdout();
let mut handle = stdout.lock();
write_line_to_fmt(&mut handle, args)
}
pub fn write_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> io::Result<()> {
let stdout = io::stdout();
let mut handle = io::BufWriter::new(stdout.lock());
for line in lines {
handle.write_all(line.as_ref().as_bytes())?;
handle.write_all(b"\n")?;
}
handle.flush()?;
Ok(())
}
pub(crate) fn write_line_human(content: &str) {
match write_line(content) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {}
Err(_) => {}
}
}
pub fn write_stderr_line_to(err: &mut impl Write, content: &str) -> io::Result<()> {
match write_line_to(err, content) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
}
}
pub fn write_stderr_line_to_fmt(
err: &mut impl Write,
args: fmt::Arguments<'_>,
) -> io::Result<()> {
match write_line_to_fmt(err, args) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
}
}
pub fn write_stderr_line(content: &str) -> io::Result<()> {
let stderr = io::stderr();
let mut handle = stderr.lock();
write_stderr_line_to(&mut handle, content)
}
pub fn write_stderr_fmt(args: fmt::Arguments<'_>) -> io::Result<()> {
let stderr = io::stderr();
let mut handle = stderr.lock();
write_stderr_line_to_fmt(&mut handle, args)
}
pub(crate) fn report_json_serialize_error(err: &impl fmt::Display) {
let _ = write_stderr_fmt(format_args!("failed to serialize JSON: {err}"));
}
pub fn print_success(message: &str) {
if is_quiet() {
return;
}
write_line_human(message);
}
pub fn print_success_fmt(args: fmt::Arguments<'_>) {
if is_quiet() {
return;
}
match write_line_fmt(args) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {}
Err(_) => {}
}
}
pub fn emit_success(
event: &str,
fields: serde_json::Value,
human: &str,
json: bool,
) -> io::Result<()> {
if json {
let envelope = SuccessEnvelope::from_value(event, fields);
json_wire::print_json_line(&envelope)?;
} else {
print_success(human);
}
Ok(())
}
pub fn emit_success_fmt(
event: &str,
fields: serde_json::Value,
human: fmt::Arguments<'_>,
json: bool,
) -> io::Result<()> {
if json {
let envelope = SuccessEnvelope::from_value(event, fields);
json_wire::print_json_line(&envelope)?;
} else {
print_success_fmt(human);
}
Ok(())
}
pub fn print_human_banner(message: &str) {
if is_quiet() || wants_json_errors() {
return;
}
if !std::io::IsTerminal::is_terminal(&std::io::stdout()) {
return;
}
write_line_human(message);
}
pub fn print_error(message: &str) -> io::Result<()> {
write_stderr_line(message)
}
pub fn print_error_fmt(args: fmt::Arguments<'_>) -> io::Result<()> {
write_stderr_fmt(args)
}
pub fn print_warning(message: &str) {
let _ = write_stderr_fmt(format_args!("warning: {message}"));
}
pub fn print_warning_fmt(args: fmt::Arguments<'_>) {
let _ = write_stderr_fmt(format_args!("warning: {args}"));
}
pub fn print_error_envelope(
exit_code: i32,
error_code: &str,
message: &str,
remote_exit_code: Option<i32>,
error_class: crate::errors::ErrorClass,
retryable: bool,
suggestion: Option<&str>,
) -> io::Result<()> {
let env = ErrorEnvelope {
exit_code,
error_code: error_code.to_string(),
message: message.to_string(),
remote_exit_code,
error_class,
retryable,
suggestion: suggestion.map(str::to_string),
};
match json_wire::print_json_line_stderr(&env) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::Other => write_stderr_fmt(format_args!(
r#"{{"exit_code":{exit_code},"message":"serialization error"}}"#
)),
Err(e) => Err(e as io::Error),
}
}
pub fn print_json_value(v: &serde_json::Value) -> io::Result<()> {
json_wire::print_json_line(v)
}