1use std::path::Path;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Error;
6
7#[derive(Debug, Serialize, Deserialize)]
8pub struct State {
9 pub meta: Meta,
10 pub priors: Priors,
11 #[serde(default)]
12 pub old_revisions: Vec<String>,
13 #[serde(default)]
14 pub new_revisions: Vec<String>,
15 #[serde(default)]
16 pub samples: Vec<Sample>,
17}
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct Meta {
21 pub tool_version: String,
22 pub started_at: String,
23}
24
25#[derive(Debug, Default, Serialize, Deserialize)]
26pub struct Priors {
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub old_pass_rate: Option<f64>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub new_pass_rate: Option<f64>,
31}
32
33#[derive(Debug, Serialize, Deserialize)]
34pub struct Sample {
35 pub revision: String,
36 pub outcome: bool,
37 pub recorded_at: String,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub comment: Option<String>,
40}
41
42pub fn read(state_dir: &Path) -> Result<State, Error> {
43 let path = state_dir.join("state.toml");
44 if !path.exists() {
45 return Err(Error::Validation(
46 "no psect session — run 'git psect start' first".into(),
47 ));
48 }
49 let contents = std::fs::read_to_string(path)?;
50 Ok(toml::from_str(&contents)?)
51}
52
53pub fn write(state_dir: &Path, state: &State) -> Result<(), Error> {
54 std::fs::create_dir_all(state_dir)?;
55 let tmp = state_dir.join("state.toml.tmp");
56 std::fs::write(&tmp, toml::to_string_pretty(state)?)?;
57 std::fs::rename(&tmp, state_dir.join("state.toml"))?;
58 Ok(())
59}