use tracing::{info, warn};
use crate::logging::XbpLogger;
pub async fn view_logs(
command_filter: Option<String>,
lines: Option<usize>,
follow: bool,
list_files: bool,
debug: bool,
) -> Result<(), String> {
let logger: XbpLogger = XbpLogger::new(debug).await?;
if list_files {
return list_log_files(&logger).await;
}
if follow {
return follow_logs(&logger, command_filter, debug).await;
}
view_recent_logs(&logger, command_filter, lines.unwrap_or(50)).await
}
async fn list_log_files(logger: &XbpLogger) -> Result<(), String> {
info!("📋 Available XBP Log Files");
info!("Log directory: {}", logger.log_dir().display());
info!("");
let files = logger.list_log_files().await?;
if files.is_empty() {
warn!("No log files found.");
return Ok(());
}
for file in files {
let metadata: std::fs::Metadata =
std::fs::metadata(&file).map_err(|e| format!("Failed to read file metadata: {}", e))?;
let size_kb: u64 = metadata.len() / 1024;
let modified: std::time::SystemTime = metadata
.modified()
.map_err(|e| format!("Failed to get modification time: {}", e))?;
let modified_str: chrono::format::DelayedFormat<chrono::format::StrftimeItems<'_>> =
chrono::DateTime::<chrono::Local>::from(modified).format("%Y-%m-%d %H:%M:%S");
info!(" {}", file.file_name().unwrap().to_string_lossy());
info!(" Size: {} KB, Modified: {}", size_kb, modified_str);
}
Ok(())
}
async fn view_recent_logs(
logger: &XbpLogger,
command_filter: Option<String>,
lines: usize,
) -> Result<(), String> {
let files = logger.list_log_files().await?;
if files.is_empty() {
warn!("No log files found.");
return Ok(());
}
let target_file: Option<std::path::PathBuf> = if let Some(command) = &command_filter {
files
.iter()
.find(|f| {
f.file_stem()
.unwrap()
.to_string_lossy()
.starts_with(command)
})
.or_else(|| files.last())
.cloned()
} else {
files
.iter()
.find(|f| f.file_stem().unwrap().to_string_lossy().starts_with("xbp-"))
.or_else(|| files.last())
.cloned()
};
let file: std::path::PathBuf = target_file.ok_or("No suitable log file found")?;
info!("📖 Viewing logs from: {}", file.display());
if let Some(cmd) = &command_filter {
info!("Filter: command = {}", cmd);
}
info!("Showing last {} lines", lines);
info!("{}", "─".repeat(80));
let log_lines: Vec<String> = logger.read_recent_logs(&file, lines).await?;
for line in log_lines {
if let Some(filter) = &command_filter {
if !line.contains(&format!("command={}", filter)) {
continue;
}
}
info!("{}", line);
}
Ok(())
}
async fn follow_logs(
logger: &XbpLogger,
command_filter: Option<String>,
_debug: bool,
) -> Result<(), String> {
info!("👁️ Following XBP logs (Press Ctrl+C to stop)");
if let Some(cmd) = &command_filter {
info!("Filter: command = {}", cmd);
}
info!("{}", "─".repeat(80));
view_recent_logs(logger, command_filter, 20).await?;
warn!("Note: Real-time following not implemented yet. Showing recent logs.");
Ok(())
}
pub fn parse_logs_args(args: &[String]) -> (Option<String>, Option<usize>, bool, bool) {
let mut command_filter: Option<String> = None;
let mut lines: Option<usize> = None;
let mut follow: bool = false;
let mut list_files: bool = false;
let mut i: usize = 0;
while i < args.len() {
match args[i].as_str() {
"--command" | "-c" => {
if i + 1 < args.len() {
command_filter = Some(args[i + 1].clone());
i += 2;
} else {
i += 1;
}
}
"--lines" | "-n" => {
if i + 1 < args.len() {
if let Ok(n) = args[i + 1].parse::<usize>() {
lines = Some(n);
}
i += 2;
} else {
i += 1;
}
}
"--follow" | "-f" => {
follow = true;
i += 1;
}
"--list" | "-l" => {
list_files = true;
i += 1;
}
_ => {
i += 1;
}
}
}
(command_filter, lines, follow, list_files)
}