blob: 2c34836648dede207f3c4139ea91507d2ba9ee29 [file] [edit]
//! Common utilities for `cargo report` commands.
use std::ffi::OsStr;
use std::path::PathBuf;
use crate::CargoResult;
use crate::GlobalContext;
use crate::compiler::CompileMode;
use crate::util::BuildLogger;
use crate::util::log_message::Target;
use crate::util::logger::RunId;
use crate::workspace::Workspace;
/// Lists log files by calling a callback for each valid log file.
///
/// * Yield log files from new to old
/// * If in a workspace, select only the log files associated with the workspace
pub fn list_log_files(
gctx: &GlobalContext,
ws: Option<&Workspace<'_>>,
) -> CargoResult<Box<dyn Iterator<Item = (PathBuf, RunId)>>> {
let log_dir = gctx.home().join("log");
let log_dir = log_dir.as_path_unlocked();
if !log_dir.exists() {
return Ok(Box::new(std::iter::empty()));
}
let walk = walkdir::WalkDir::new(log_dir)
.follow_links(true)
.sort_by(|a, b| a.file_name().cmp(b.file_name()).reverse())
.min_depth(1)
.max_depth(1)
.into_iter()
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
// We only accept JSONL/NDJSON files.
if !entry.file_type().is_file() {
return None;
}
if entry.path().extension() != Some(OsStr::new("jsonl")) {
return None;
}
// ...and the file name must follow RunId format
let run_id = path.file_stem()?.to_str()?.parse::<RunId>().ok()?;
Some((path.to_path_buf(), run_id))
});
let ws_run_id = ws.map(BuildLogger::generate_run_id);
let walk = walk.filter(move |(_, id)| {
ws_run_id
.as_ref()
.map_or(true, |ws_id| id.same_workspace(ws_id))
});
Ok(Box::new(walk))
}
pub fn find_log_file(
gctx: &GlobalContext,
ws: Option<&Workspace<'_>>,
id: Option<&RunId>,
) -> CargoResult<Option<(std::path::PathBuf, RunId)>> {
match id {
Some(requested_id) => {
for (path, run_id) in list_log_files(gctx, ws)? {
if run_id.to_string() == requested_id.to_string() {
return Ok(Some((path, run_id)));
}
}
Ok(None)
}
None => Ok(list_log_files(gctx, ws)?.next()),
}
}
pub fn unit_target_description(target: &Target, mode: CompileMode) -> String {
// This is pretty similar to how the current `compiler::timings`
// renders `workspace::manifest::Target`. However, our target is
// a simplified type so we cannot reuse the same logic here.
let mut target_str =
if target.kind == "lib" && matches!(mode, CompileMode::Build | CompileMode::Check { .. }) {
// Special case for brevity, since most dependencies hit this path.
"".to_string()
} else if target.kind == "build-script" {
" build-script".to_string()
} else {
format!(r#" {} "{}""#, target.name, target.kind)
};
match mode {
CompileMode::Test => target_str.push_str(" (test)"),
CompileMode::Build => {}
CompileMode::Check { test: true } => target_str.push_str(" (check-test)"),
CompileMode::Check { test: false } => target_str.push_str(" (check)"),
CompileMode::Doc { .. } => target_str.push_str(" (doc)"),
CompileMode::Doctest => target_str.push_str(" (doc test)"),
CompileMode::Docscrape => target_str.push_str(" (doc scrape)"),
CompileMode::RunCustomBuild => target_str.push_str(" (run)"),
}
target_str
}