use std::path::PathBuf;
use crate::config::{LogHandlers, LogHandler, LogHandlerType};
use crate::execution::{ExecutionContext};
use anyhow::{format_err, Result};
use crate::utils::resolve_cwd;
#[derive(Debug, Clone, Copy)]
pub enum InputStream {
Stdout,
Stderr,
Both,
None,
}
impl InputStream {
#[allow(dead_code)]
pub fn is_stdout(&self) -> bool {
!matches!(self, InputStream::Stderr)
}
#[allow(dead_code)]
pub fn is_stderr(&self) -> bool {
!matches!(self, InputStream::Stdout)
}
pub fn is_compatible(&self, other: Self) -> bool {
match (self, other) {
(InputStream::Both, _) => true,
(InputStream::Stdout, InputStream::Stdout) => true,
(InputStream::Stderr, InputStream::Stderr) => true,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct FileOutputSpec {
pub file: PathBuf,
pub append: bool,
}
#[derive(Debug, Clone)]
pub enum OutputStreamSpec {
Stdout,
#[allow(dead_code)]
Stderr,
File(FileOutputSpec),
}
#[derive(Debug, Clone)]
pub struct PipeSpec {
pub input: InputStream,
pub output: OutputStreamSpec,
}
impl PipeSpec {
pub fn from_handler(handler: &LogHandler, input: InputStream, context: &ExecutionContext) -> Result<Vec<Self>> {
if !handler.options.hooks && context.is_in_hook() {
return Ok(Vec::new());
}
match &handler.handler {
LogHandlerType::File(f) => {
if context.options.dry_run {
return Ok(Vec::new());
}
let mut file = resolve_cwd(&context.log_dir, f.output.as_ref());
if f.split {
if file.is_file() {
return Err(format_err!("Cannot create directory '{}'", file.display()));
}
std::fs::create_dir_all(&file)?;
let filename = if context.is_in_hook() {
context.focus.clone().unwrap_or_else(|| "job".to_string())
} else {
context.current_command_id().clone()
};
file.push(format!("{}.log", filename));
}
Ok(vec![Self {
input,
output: OutputStreamSpec::File(FileOutputSpec {
file,
append: true,
}),
}])
},
LogHandlerType::Console => {
Ok(vec![Self {
input,
output: OutputStreamSpec::Stdout,
}])
},
}
}
}
#[derive(Debug, Clone)]
pub struct LoggingSpec {
pub pipes: Vec<PipeSpec>,
}
impl LoggingSpec {
pub fn from_config(config: &LogHandlers, context: &ExecutionContext) -> Result<Self> {
let mut result = Vec::new();
for handler in config {
let input_stream = if handler.options.stdout && handler.options.stderr {
InputStream::Both
} else if handler.options.stdout {
InputStream::Stdout
} else if handler.options.stderr {
InputStream::Stderr
} else {
InputStream::None
};
result.extend(PipeSpec::from_handler(handler, input_stream, context)?);
}
Ok(Self {
pipes: result,
})
}
}