| //! 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 |
| } |