1use camino::Utf8PathBuf;
2use serde::{Deserialize, Serialize};
3use std::io::Write;
4
5#[derive(Deserialize, Serialize, Debug, Clone)]
7pub struct CheckpointData {
8 pub file: String,
9 pub line: usize,
10}
11
12impl CheckpointData {
13 pub async fn load(path: &Utf8PathBuf) -> anyhow::Result<Option<Self>> {
16 match tokio::fs::read(path.as_std_path()).await {
17 Ok(bytes) => {
18 let data: Self = serde_json::from_slice(&bytes)?;
19 Ok(Some(data))
20 }
21 Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
22 Err(err) => Err(err.into()),
23 }
24 }
25
26 pub fn save_atomic(
29 checkpoint_path: &Utf8PathBuf,
30 file: &Utf8PathBuf,
31 line: usize,
32 ) -> anyhow::Result<()> {
33 let data = Self {
34 file: file.to_string(),
35 line,
36 };
37 let json = serde_json::to_string(&data)?;
38 let dir = checkpoint_path
39 .parent()
40 .map(|p| p.as_std_path())
41 .unwrap_or_else(|| std::path::Path::new("."));
42 let prefix = checkpoint_path.file_name().unwrap_or(".checkpoint");
43 let mut tmp = tempfile::Builder::new().prefix(prefix).tempfile_in(dir)?;
44 tmp.write_all(json.as_bytes())?;
45 tmp.persist(checkpoint_path.as_std_path())?;
46 Ok(())
47 }
48}