use hurl_core::ast::SourceInfo;
use hurl_core::error::{DisplaySourceError, OutputFormat};
use hurl_core::input::Input;
use hurl_core::text::{Format, Style, StyledString};
use crate::runner::Value;
use crate::util::redacted::Redact;
use crate::util::term::{Stderr, WriteMode};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ErrorFormat {
Short,
Long,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Verbosity {
LowVerbose,
Verbose,
VeryVerbose,
}
#[derive(Clone)]
pub struct Logger {
pub(crate) color: bool,
pub(crate) error_format: ErrorFormat,
pub(crate) verbosity: Option<Verbosity>,
pub(crate) stderr: Stderr,
secrets: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LoggerOptions {
color: bool,
error_format: ErrorFormat,
verbosity: Option<Verbosity>,
}
pub struct LoggerOptionsBuilder {
color: bool,
error_format: ErrorFormat,
verbosity: Option<Verbosity>,
}
impl LoggerOptionsBuilder {
pub fn new() -> Self {
LoggerOptionsBuilder::default()
}
pub fn color(&mut self, color: bool) -> &mut Self {
self.color = color;
self
}
pub fn error_format(&mut self, error_format: ErrorFormat) -> &mut Self {
self.error_format = error_format;
self
}
pub fn verbosity(&mut self, verbosity: Option<Verbosity>) -> &mut Self {
self.verbosity = verbosity;
self
}
pub fn build(&self) -> LoggerOptions {
LoggerOptions {
color: self.color,
error_format: self.error_format,
verbosity: self.verbosity,
}
}
}
impl Default for LoggerOptionsBuilder {
fn default() -> Self {
LoggerOptionsBuilder {
color: false,
error_format: ErrorFormat::Short,
verbosity: None,
}
}
}
impl Logger {
pub fn new(options: &LoggerOptions, term: Stderr, secrets: &[String]) -> Self {
Logger {
color: options.color,
error_format: options.error_format,
verbosity: options.verbosity,
stderr: term,
secrets: secrets.to_vec(),
}
}
fn format(&self) -> Format {
if self.color {
Format::Ansi
} else {
Format::Plain
}
}
pub fn info(&mut self, message: &str) {
self.eprintln(message);
}
pub fn info_curl_cmd(&mut self, cmd: &str) {
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("*", Style::new().blue().bold());
s.push(" ");
s.push("Request can be run with the following curl command:\n");
s.push_with("*", Style::new().blue().bold());
s.push(" ");
s.push(cmd);
self.eprintln(&s.to_string(fmt));
}
pub fn debug(&mut self, message: &str) {
if self.verbosity.is_none() || self.verbosity == Some(Verbosity::LowVerbose) {
return;
}
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("*", Style::new().blue().bold());
if !message.is_empty() {
s.push(" ");
s.push(message);
}
self.eprintln(&s.to_string(fmt));
}
pub fn debug_important(&mut self, message: &str) {
if self.verbosity.is_none() || self.verbosity == Some(Verbosity::LowVerbose) {
return;
}
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("*", Style::new().blue().bold());
if !message.is_empty() {
s.push(" ");
s.push_with(message, Style::new().bold());
}
self.eprintln(&s.to_string(fmt));
}
pub fn debug_curl(&mut self, message: &str) {
if self.verbosity.is_none() || self.verbosity == Some(Verbosity::LowVerbose) {
return;
}
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("**", Style::new().blue().bold());
if !message.is_empty() {
s.push(" ");
s.push(message);
}
self.eprintln(&s.to_string(fmt));
}
pub fn debug_error<E: DisplaySourceError>(
&mut self,
content: &str,
filename: Option<&Input>,
error: &E,
entry_src_info: SourceInfo,
) {
if self.verbosity.is_none() {
return;
}
let filename = filename.map_or(String::new(), |f| f.to_string());
let message = error.render(
&filename,
content,
Some(entry_src_info),
OutputFormat::Terminal(self.color),
);
message.lines().for_each(|l| self.debug(l));
}
pub fn debug_headers_in(&mut self, headers: &[(&str, &str)]) {
if self.verbosity.is_none() {
return;
}
let fmt = self.format();
for (name, value) in headers {
let mut s = StyledString::new();
s.push("< ");
s.push_with(name, Style::new().cyan().bold());
s.push(": ");
s.push(value);
self.eprintln(&s.to_string(fmt));
}
self.eprintln("<");
}
pub fn debug_headers_out(&mut self, headers: &[(&str, &str)]) {
if self.verbosity.is_none() {
return;
}
let fmt = self.format();
for (name, value) in headers {
let mut s = StyledString::new();
s.push("> ");
s.push_with(name, Style::new().cyan().bold());
s.push(": ");
s.push(value);
self.eprintln(&s.to_string(fmt));
}
self.eprintln(">");
}
pub fn debug_status_version_in(&mut self, line: &str) {
if self.verbosity.is_none() {
return;
}
let fmt = self.format();
let mut s = StyledString::new();
s.push("< ");
s.push_with(line, Style::new().green().bold());
self.eprintln(&s.to_string(fmt));
}
pub fn warning(&mut self, message: &str) {
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("warning", Style::new().yellow().bold());
s.push(": ");
s.push_with(message, Style::new().bold());
self.eprintln(&s.to_string(fmt));
}
pub fn error_rich(&mut self, message: &str) {
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("error", Style::new().red().bold());
s.push(": ");
s.push(message);
s.push("\n");
self.eprintln(&s.to_string(fmt));
}
pub fn debug_method_version_out(&mut self, line: &str) {
if self.verbosity.is_none() {
return;
}
let fmt = self.format();
let mut s = StyledString::new();
s.push("> ");
s.push_with(line, Style::new().purple().bold());
self.eprintln(&s.to_string(fmt));
}
pub fn capture(&mut self, name: &str, value: &Value) {
if self.verbosity.is_none() || self.verbosity == Some(Verbosity::LowVerbose) {
return;
}
let value = value.to_string();
let fmt = self.format();
let mut s = StyledString::new();
s.push_with("*", Style::new().blue().bold());
s.push(" ");
s.push_with(name, Style::new().yellow().bold());
s.push(": ");
s.push(&value);
self.eprintln(&s.to_string(fmt));
}
pub fn set_secrets(&mut self, secrets: Vec<String>) {
if self.secrets == secrets {
return;
}
self.secrets = secrets;
if matches!(self.stderr.mode(), WriteMode::Buffered) {
let old_buffer = self.stderr.buffer();
let new_buffer = old_buffer.redact(&self.secrets);
self.stderr.set_buffer(new_buffer);
}
}
fn eprintln(&mut self, message: &str) {
if self.secrets.is_empty() {
self.stderr.eprintln(message);
return;
}
let redacted = message.redact(&self.secrets);
self.stderr.eprintln(&redacted);
}
}