diff options
Diffstat (limited to 'mingling_ci/src/reporter.rs')
| -rw-r--r-- | mingling_ci/src/reporter.rs | 208 |
1 files changed, 0 insertions, 208 deletions
diff --git a/mingling_ci/src/reporter.rs b/mingling_ci/src/reporter.rs deleted file mode 100644 index 1a1ac08..0000000 --- a/mingling_ci/src/reporter.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! Minimal log exporter for CI reports. -//! -//! Writes per-package results into `collect/{task}/{platform}/{package}.{ok|err}` -//! so that the [`crate::cmd::collect_results`] command can assemble the final -//! report. The task name is set once per CI phase via [`set_task`]. - -use std::collections::HashMap; -use std::fs; -use std::path::Path; -use std::sync::{LazyLock, Mutex}; - -/// Root of the collected CI logs (relative to the repo root). -pub const COLLECT_DIR: &str = "./.temp/reports/collect"; - -/// Generated report output (relative to the repo root). -pub const REPORT_PATH: &str = "./.temp/reports/result.md"; - -/// The platform a package check ran on. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum ReportPlatform { - Windows, - Linux, - MacOS, -} - -impl ReportPlatform { - /// Directory name used under the task folder. - const fn dir_name(self) -> &'static str { - match self { - Self::Windows => "Windows", - Self::Linux => "Linux", - Self::MacOS => "MacOS", - } - } -} - -/// The outcome of a package check. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ReportResult { - /// Check passed. - Ok, - /// Check failed, with the captured output. - Error(String), -} - -/// Current task name (e.g. `Build-All`); set via [`set_task`]. -static CURRENT_TASK: Mutex<Option<String>> = Mutex::new(None); - -/// Pending success entries: `(item, location)`. -type PendingOk = (String, String); - -/// Successful items pending a [`flush`], grouped by platform. -static OK_BUFFER: LazyLock<Mutex<HashMap<ReportPlatform, Vec<PendingOk>>>> = - LazyLock::new(|| Mutex::new(HashMap::new())); - -/// Sets the task that subsequent [`export`] calls write under. -/// -/// # Panics -/// -/// Panics if the internal mutex is poisoned. -pub fn set_task(task: &str) { - *CURRENT_TASK.lock().unwrap() = Some(task.to_string()); -} - -/// Exports one item result. -/// -/// `item` and `location` are free-form strings chosen by the generator. -/// Successes are buffered and written to the `ok` file by [`flush`]; failures -/// write `{task}.{platform}.{item}.err` immediately (first line is the -/// location). Errors are reported to stderr and otherwise ignored. -/// -/// # Panics -/// -/// Panics if the internal task mutex is poisoned. -pub fn export(item: &str, location: &str, result: ReportResult) { - export_on(item, location, current_platform(), result); -} - -/// The `ReportPlatform` for the currently compiling target. -fn current_platform() -> ReportPlatform { - if cfg!(target_os = "windows") { - ReportPlatform::Windows - } else if cfg!(target_os = "macos") { - ReportPlatform::MacOS - } else { - ReportPlatform::Linux - } -} - -/// Exports one item result for a specific platform. -/// -/// `item` and `location` are free-form strings chosen by the generator. -/// Successes are buffered and written to the `ok` file by [`flush`]; failures -/// write `{task}.{platform}.{item}.err` immediately (first line is the -/// location). Errors are reported to stderr and otherwise ignored. -/// -/// # Panics -/// -/// Panics if the internal task mutex is poisoned. -pub fn export_on(item: &str, location: &str, platform: ReportPlatform, result: ReportResult) { - match result { - ReportResult::Ok => OK_BUFFER - .lock() - .unwrap() - .entry(platform) - .or_default() - .push((item.to_string(), location.to_string())), - ReportResult::Error(output) => write_err(item, location, platform, &output), - } -} - -/// Writes buffered successes to `collect/{task}.{platform}.ok`, one `item` (or -/// `item = location`) per line. -/// -/// # Panics -/// -/// Panics if the internal task mutex is poisoned. -pub fn flush() { - let Some(task) = CURRENT_TASK.lock().unwrap().clone() else { - eprintln!("reporter: no current task; call reporter::set_task first"); - return; - }; - - let buffered = std::mem::take(&mut *OK_BUFFER.lock().unwrap()); - if buffered.is_empty() { - return; - } - - if let Err(e) = fs::create_dir_all(COLLECT_DIR) { - eprintln!("reporter: failed to create {COLLECT_DIR}: {e}"); - return; - } - - for (platform, items) in buffered { - let lines: Vec<String> = items - .iter() - .map(|(item, location)| { - if location.is_empty() { - item.clone() - } else { - format!("{item} = {location}") - } - }) - .collect(); - let content = if lines.is_empty() { - String::new() - } else { - lines.join("\n") + "\n" - }; - let platform_name = platform.dir_name(); - let path = Path::new(COLLECT_DIR).join(format!("{task}.{platform_name}.ok")); - if let Err(e) = fs::write(&path, content) { - eprintln!("reporter: failed to write {}: {e}", path.display()); - } - } -} - -/// Writes a failure entry to `collect/{task}.{platform}.{item}.err`, with the -/// location as the first line (empty when unknown). -fn write_err(item: &str, location: &str, platform: ReportPlatform, output: &str) { - let Some(task) = CURRENT_TASK.lock().unwrap().clone() else { - eprintln!("reporter: no current task; call reporter::set_task first"); - return; - }; - - if let Err(e) = fs::create_dir_all(COLLECT_DIR) { - eprintln!("reporter: failed to create {COLLECT_DIR}: {e}"); - return; - } - - let platform_name = platform.dir_name(); - let path = Path::new(COLLECT_DIR).join(format!("{task}.{platform_name}.{item}.err")); - if let Err(e) = fs::write(&path, format!("{location}\n{output}")) { - eprintln!("reporter: failed to write {}: {e}", path.display()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn export_writes_ok_and_err_files() { - set_task("reporter-test"); - let platform_name = current_platform().dir_name(); - let ok_path = Path::new(COLLECT_DIR).join(format!("reporter-test.{platform_name}.ok")); - let err_path = - Path::new(COLLECT_DIR).join(format!("reporter-test.{platform_name}.pkg-b.err")); - fs::remove_file(&ok_path).ok(); - fs::remove_file(&err_path).ok(); - - export("pkg-a", "./pkg-a", ReportResult::Ok); - export("pkg-b", "./pkg-b", ReportResult::Error("boom".to_string())); - export("pkg-c", "", ReportResult::Ok); // no location - flush(); - - assert!(ok_path.is_file()); - assert_eq!( - fs::read_to_string(&ok_path).unwrap(), - "pkg-a = ./pkg-a\npkg-c\n" - ); - assert!(err_path.is_file()); - assert_eq!(fs::read_to_string(&err_path).unwrap(), "./pkg-b\nboom"); - - fs::remove_file(ok_path).ok(); - fs::remove_file(err_path).ok(); - } -} |
