git_perf/
config.rs

1use anyhow::Result;
2use std::{
3    fs::File,
4    io::{Read, Write},
5};
6use toml_edit::{value, Document};
7
8use crate::git_interop::get_head_revision;
9
10// TODO(kaihowl) proper error handling
11pub fn write_config(conf: &str) {
12    let mut f = File::create(".gitperfconfig").expect("open file for writing failed");
13    f.write_all(conf.as_bytes()).expect("failed to write");
14}
15
16pub fn read_config() -> Option<String> {
17    read_config_from_file(".gitperfconfig")
18}
19
20// TODO(kaihowl) proper error handling
21// TODO(kaihowl) proper file type
22fn read_config_from_file(file: &str) -> Option<String> {
23    let mut conf_str = String::new();
24    File::open(file).ok()?.read_to_string(&mut conf_str).ok()?;
25    Some(conf_str)
26}
27
28pub fn determine_epoch_from_config(measurement: &str) -> Option<u32> {
29    // TODO(hoewelmk) configure path, use different working directory than repo root
30    let conf = read_config()?;
31    determine_epoch(measurement, &conf)
32}
33
34fn determine_epoch(measurement: &str, conf_str: &str) -> Option<u32> {
35    // TODO(kaihowl) buffered reading?
36    let config = conf_str
37        .parse::<Document>()
38        .expect("Failed to parse config");
39
40    let get_epoch = |section: &str| {
41        let s = config
42            .get("measurement")?
43            .get(section)?
44            .get("epoch")?
45            .as_str()?;
46        u32::from_str_radix(s, 16).ok()
47    };
48
49    get_epoch(measurement).or_else(|| get_epoch("*"))
50}
51
52pub fn bump_epoch_in_conf(measurement: &str, conf_str: &mut String) -> Result<()> {
53    let mut conf = conf_str
54        .parse::<Document>()
55        .expect("failed to parse config");
56
57    let head_revision = get_head_revision()?;
58    // TODO(kaihowl) ensure that always non-inline tables are written in an empty config file
59    conf["measurement"][measurement]["epoch"] = value(&head_revision[0..8]);
60    *conf_str = conf.to_string();
61
62    Ok(())
63}
64
65// TODO(kaihowl) proper error handling
66pub fn bump_epoch(measurement: &str) -> Result<()> {
67    let mut conf_str = read_config().unwrap_or_default();
68    bump_epoch_in_conf(measurement, &mut conf_str)?;
69    write_config(&conf_str);
70    Ok(())
71}
72
73#[cfg(test)]
74mod test {
75    use super::*;
76
77    #[test]
78    fn test_read_epochs() {
79        // TODO(hoewelmk) order unspecified in serialization...
80        let configfile = r#"[measurement."something"]
81#My comment
82epoch="34567898"
83
84[measurement."somethingelse"]
85epoch="a3dead"
86
87[measurement."*"]
88# General performance regression
89epoch="12344555"
90"#;
91
92        let epoch = determine_epoch("something", configfile);
93        assert_eq!(epoch, Some(0x34567898));
94
95        let epoch = determine_epoch("somethingelse", configfile);
96        assert_eq!(epoch, Some(0xa3dead));
97
98        let epoch = determine_epoch("unspecified", configfile);
99        assert_eq!(epoch, Some(0x12344555));
100    }
101
102    #[test]
103    fn test_bump_epochs() {
104        let configfile = r#"[measurement."something"]
105#My comment
106epoch = "34567898"
107"#;
108
109        let mut actual = String::from(configfile);
110        bump_epoch_in_conf("something", &mut actual).expect("Failed to bump epoch");
111
112        let expected = format!(
113            r#"[measurement."something"]
114#My comment
115epoch = "{}"
116"#,
117            &get_head_revision().expect("get_head_revision failed")[0..8],
118        );
119
120        assert_eq!(actual, expected);
121    }
122
123    #[test]
124    fn test_bump_new_epoch_and_read_it() {
125        let mut conf = String::new();
126        bump_epoch_in_conf("mymeasurement", &mut conf).expect("Failed to bump epoch");
127        let epoch = determine_epoch("mymeasurement", &conf);
128        assert!(epoch.is_some());
129    }
130
131    #[test]
132    fn test_parsing() {
133        let toml_str = r#"
134        measurement = { test2 = { epoch = "834ae670e2ecd5c87020fde23378b890832d6076" } }
135    "#;
136
137        let doc = toml_str.parse::<Document>().expect("sfdfdf");
138
139        let measurement = "test";
140
141        if let Some(e) = doc
142            .get("measurement")
143            .and_then(|m| m.get(measurement))
144            .and_then(|m| m.get("epoch"))
145        {
146            println!("YAY: {}", e);
147            panic!("stuff");
148        }
149    }
150}