pub struct StatePersistence {
state_file: PathBuf,
state: Arc<RwLock<AgentState>>,
auto_save_interval: u64,
}
impl StatePersistence {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn new(state_dir: impl AsRef<Path>) -> Result<Self> {
let state_file = state_dir.as_ref().join("agent_state.json");
let state = if state_file.exists() {
Self::load_from_file(&state_file)?
} else {
AgentState::default()
};
Ok(Self {
state_file,
state: Arc::new(RwLock::new(state)),
auto_save_interval: 60, })
}
fn load_from_file(path: &Path) -> Result<AgentState> {
let contents = std::fs::read_to_string(path).context("Failed to read state file")?;
serde_json::from_str(&contents).context("Failed to deserialize state")
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn save(&self) -> Result<()> {
let state = self.state.read().await;
let json = serde_json::to_string_pretty(&*state)?;
if let Some(parent) = self.state_file.parent() {
fs::create_dir_all(parent).await?;
}
let temp_file = self.state_file.with_extension("tmp");
fs::write(&temp_file, json).await?;
fs::rename(&temp_file, &self.state_file).await?;
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn start_auto_save(&self) {
let state_file = self.state_file.clone();
let state = self.state.clone();
let interval = self.auto_save_interval;
tokio::spawn(async move {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(interval));
loop {
interval.tick().await;
if let Ok(state) = state.read().await.to_json() {
if let Err(e) = fs::write(&state_file, state).await {
tracing::error!("Failed to auto-save state: {}", e);
} else {
tracing::debug!("State auto-saved");
}
}
}
});
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn add_project(&self, project: ProjectState) -> Result<()> {
let mut state = self.state.write().await;
state.monitored_projects.insert(project.id.clone(), project);
state.last_updated = Utc::now();
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn remove_project(&self, project_id: &str) -> Result<()> {
let mut state = self.state.write().await;
state.monitored_projects.remove(project_id);
state.last_updated = Utc::now();
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn update_metrics(&self, project_id: &str, metrics: QualityMetrics) -> Result<()> {
let mut state = self.state.write().await;
if let Some(project) = state.monitored_projects.get_mut(project_id) {
project.current_metrics = metrics.clone();
project.last_analyzed = Some(Utc::now());
state.quality_history.push(QualitySnapshot {
timestamp: Utc::now(),
project_id: project_id.to_string(),
metrics,
violations: Vec::new(),
});
if state.quality_history.len() > 1000 {
let drain_count = state.quality_history.len() - 1000;
state.quality_history.drain(0..drain_count);
}
state.last_updated = Utc::now();
}
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn get_state(&self) -> AgentState {
self.state.read().await.clone()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn update_statistics<F>(&self, updater: F) -> Result<()>
where
F: FnOnce(&mut AgentStatistics),
{
let mut state = self.state.write().await;
updater(&mut state.statistics);
state.last_updated = Utc::now();
Ok(())
}
}