use std::collections::HashSet;
use std::fs::{self, File, OpenOptions};
use std::io::Write;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use crate::checklist::{ChecklistItem, VerifyMethod, CHECKLIST};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
Pass,
Fail,
Skip,
NotRun,
}
impl Verdict {
fn as_str(self) -> &'static str {
match self {
Verdict::Pass => "PASS",
Verdict::Fail => "FAIL",
Verdict::Skip => "SKIP",
Verdict::NotRun => "NOTRUN",
}
}
}
pub struct VerifyLog {
file: File,
path: String,
start: Instant,
pass: u32,
fail: u32,
skip: u32,
notrun: u32,
logged_ids: HashSet<u32>,
problems: Vec<(u32, &'static str, &'static str, String)>, }
impl VerifyLog {
pub fn new() -> Self {
fs::create_dir_all("検証ログ").expect("検証ログディレクトリの作成に失敗しました");
let epoch = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
let path = format!("検証ログ/verify_all_{epoch}.log");
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&path)
.expect("ログファイルを開けませんでした");
writeln!(file, "=== rustraight verify_all run (epoch={epoch}) ===").ok();
file.flush().ok();
println!("検証ログ: {path}");
Self {
file,
path,
start: Instant::now(),
pass: 0,
fail: 0,
skip: 0,
notrun: 0,
logged_ids: HashSet::new(),
problems: Vec::new(),
}
}
pub fn record(&mut self, item: &ChecklistItem, verdict: Verdict, reason: &str) {
if !self.logged_ids.insert(item.id) {
return; }
let elapsed = self.start.elapsed().as_secs_f32();
writeln!(
self.file,
"t+{elapsed:>8.2}s cat={:?} id={:>3} api=\"{}\" verdict={} reason=\"{}\"",
item.category,
item.id,
item.api,
verdict.as_str(),
reason
)
.ok();
self.file.flush().ok();
match verdict {
Verdict::Pass => self.pass += 1,
Verdict::Fail => {
self.fail += 1;
self.problems.push((item.id, item.api, "FAIL", reason.to_string()));
}
Verdict::Skip => {
self.skip += 1;
self.problems.push((item.id, item.api, "SKIP", reason.to_string()));
}
Verdict::NotRun => {
self.notrun += 1;
self.problems.push((item.id, item.api, "NOTRUN", reason.to_string()));
}
}
}
pub fn summary_counts(&self) -> (u32, u32, u32, u32) {
(self.pass, self.fail, self.skip, self.notrun)
}
pub fn path(&self) -> &str {
&self.path
}
pub fn sweep_notrun_all(&mut self) {
let remaining: Vec<&'static ChecklistItem> = CHECKLIST
.iter()
.filter(|i| i.method != VerifyMethod::Auto && !self.logged_ids.contains(&i.id))
.collect();
for item in remaining {
self.record(item, Verdict::NotRun, "ツール終了のため未実施");
}
}
pub fn write_summary(&mut self) {
let elapsed = self.start.elapsed().as_secs_f32();
let total = self.pass + self.fail + self.skip + self.notrun;
writeln!(self.file, "=== summary at t+{elapsed:.2}s ===").ok();
writeln!(
self.file,
"PASS={} FAIL={} SKIP={} NOTRUN={} TOTAL={}",
self.pass, self.fail, self.skip, self.notrun, total
)
.ok();
for (id, api, kind, reason) in &self.problems {
writeln!(self.file, " [{kind}] id={id} api=\"{api}\" reason=\"{reason}\"").ok();
}
self.file.flush().ok();
}
}