use crate::store::Store;
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SelectedList {
pub commit: String, pub changed: Vec<String>, pub selected: Vec<String>, }
fn str_array(obj: &serde_json::Map<String, Value>, k: &str) -> Result<Vec<String>, String> {
match obj.get(k) {
None => Ok(Vec::new()),
Some(Value::Array(a)) => a
.iter()
.map(|e| {
e.as_str()
.map(|s| s.to_string())
.ok_or(format!("selected-list.{k} element is not a string"))
})
.collect(),
Some(_) => Err(format!("selected-list.{k} must be an array")),
}
}
pub fn from_value(v: &Value) -> Result<SelectedList, String> {
let obj = v.as_object().ok_or("selected-list is not an object")?;
crate::tick::only_keys(obj, &["commit", "changed", "selected"], "selected-list")?;
let commit = obj
.get("commit")
.and_then(|x| x.as_str())
.unwrap_or("")
.to_string();
Ok(SelectedList {
commit,
changed: str_array(obj, "changed")?,
selected: str_array(obj, "selected")?,
})
}
pub fn read(store: &Store) -> std::io::Result<Option<SelectedList>> {
let path = store.root.join("results").join("selected.json");
let text = match std::fs::read_to_string(&path) {
Ok(t) => t,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(e) => return Err(e),
};
let v: Value = serde_json::from_str(&text)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
from_value(&v)
.map(Some)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::store::Store;
fn store() -> (std::path::PathBuf, Store) {
use std::sync::atomic::{AtomicU64, Ordering};
static N: AtomicU64 = AtomicU64::new(0);
let p = std::env::temp_dir().join(format!(
"ev-selected-{}-{}",
std::process::id(),
N.fetch_add(1, Ordering::Relaxed)
));
let _ = std::fs::remove_dir_all(&p);
std::fs::create_dir_all(&p).unwrap();
let s = Store::at(&p);
s.init().unwrap();
(p, s)
}
#[test]
fn read_should_parse_the_list_when_results_selected_json_exists() {
let (_p, s) = store();
std::fs::write(
s.root.join("results").join("selected.json"),
r#"{"commit":"d308afac1b2c3d4e5f60718293a4b5c6d7e8f901","changed":["pyproject.toml"],"selected":["pytest x"]}"#,
)
.unwrap();
let sl = read(&s).unwrap().expect("present");
assert_eq!(sl.changed, vec!["pyproject.toml".to_string()]);
assert_eq!(sl.selected, vec!["pytest x".to_string()]);
}
#[test]
fn read_should_be_none_when_no_selected_list_exists() {
let (_p, s) = store();
let sl = read(&s).unwrap();
assert!(sl.is_none());
}
#[test]
fn from_value_should_reject_the_list_when_it_has_an_unknown_field() {
let v = serde_json::json!({ "commit": "x", "selected": [], "health": 1 });
let parsed = from_value(&v);
assert!(parsed.is_err());
}
}