use std::path::{Path, PathBuf};
use std::{fs, io};
use serde::{Deserialize, Serialize};
use crate::runs::{RunsError, write_atomic};
pub const HUB_DIR: &str = "hub";
pub const PUSH_FILE: &str = "push.json";
#[derive(Debug, thiserror::Error)]
pub enum RecordError {
#[error("cannot create {}", path.display())]
Dir {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("cannot serialize the push record")]
Serialize(#[from] serde_json::Error),
#[error(transparent)]
Write(#[from] RunsError),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct PushRecord {
pub repo: String,
pub commit: String,
pub url: String,
pub files: Vec<String>,
pub private: bool,
pub pushed: String,
}
impl PushRecord {
pub fn save(&self, run_dir: &Path) -> Result<(), RecordError> {
let dir = run_dir.join(HUB_DIR);
fs::create_dir_all(&dir).map_err(|source| RecordError::Dir {
path: dir.clone(),
source,
})?;
let mut content = serde_json::to_vec_pretty(self)?;
content.push(b'\n');
write_atomic(&dir, PUSH_FILE, &content)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_saved_record_reads_back() -> Result<(), Box<dyn std::error::Error>> {
let dir = tempfile::tempdir()?;
let record = PushRecord {
repo: "me/x".into(),
commit: "1".into(),
url: "https://hf.co/me/x/commit/1".into(),
files: vec!["adapter_config.json".into(), "README.md".into()],
private: true,
pushed: "2026-10-04T12:00:00Z".into(),
};
record.save(dir.path())?;
let text = fs::read_to_string(dir.path().join("hub/push.json"))?;
assert_eq!(serde_json::from_str::<PushRecord>(&text)?, record);
Ok(())
}
}