1use std::{
2 fs::File,
3 io::{BufRead, BufReader, BufWriter, Write as _},
4 path::Path,
5};
6
7use anyhow::Context;
8use chrono::Utc;
9use fd_lock::RwLock;
10use serde::{Deserialize, Serialize};
11
12const LOG_FILE_NAME: &str = "hope-log.jsonl";
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15pub enum CacheLogLine {
16 PulledCrateOutputs(PullCrateOutputsEvent),
17 PushedCrateOutputs(PushCrateOutputsEvent),
18 RanBuildScript(BuildScriptRunEvent),
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone)]
22pub struct PullCrateOutputsEvent {
23 pub crate_unit_name: String,
24 pub copied_at: chrono::DateTime<Utc>,
25 pub copied_from: String,
28 pub duration_secs: f64,
30}
31
32#[derive(Debug, Serialize, Deserialize, Clone)]
33pub struct PushCrateOutputsEvent {
34 pub crate_unit_name: String,
35 pub copied_at: chrono::DateTime<Utc>,
36 pub copied_from: String,
39 pub duration_secs: f64,
41}
42
43#[derive(Debug, Serialize, Deserialize, Clone)]
47pub struct BuildScriptRunEvent {
48 pub ran_at: chrono::DateTime<Utc>,
50}
51
52pub fn write_log_line(cache_dir: &Path, log_line: CacheLogLine) -> anyhow::Result<()> {
53 let file = File::options()
54 .create(true)
55 .append(true)
56 .open(cache_dir.join(LOG_FILE_NAME))?;
57 let mut file = RwLock::new(file);
58 let mut write_guard = file.write()?;
59 let mut writer = BufWriter::new(&mut *write_guard);
60 serde_json::to_writer(&mut writer, &log_line)?;
61 writeln!(&mut writer)?;
62 writer.flush()?;
63
64 Ok(())
65}
66
67pub fn read_log(cache_dir: &Path) -> anyhow::Result<Vec<CacheLogLine>> {
68 let mut log = Vec::new();
69 let file = File::open(cache_dir.join(LOG_FILE_NAME))?;
70 let mut file = RwLock::new(file);
71 let mut read_guard = file.write()?;
72 let reader = BufReader::new(&mut *read_guard);
73
74 for line in reader.lines() {
75 let line = line?;
76 log.push(
77 serde_json::from_str(&line)
78 .with_context(|| format!("Failed to deserialize log line:\n{line}"))?,
79 );
80 }
81 Ok(log)
82}