use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use super::tree::{Fold, Node, NodeId, Show, path_to};
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct Saved {
#[serde(default)]
pub show: Show,
#[serde(default)]
pub expanded: Vec<(NodeId, bool)>,
}
impl Saved {
pub fn from_fold(fold: &Fold, roots: &[Node]) -> Self {
let mut expanded: Vec<(NodeId, bool)> = fold
.expanded
.iter()
.filter(|(id, _)| roots.is_empty() || path_to(roots, id).is_some())
.map(|(id, expanded)| (id.clone(), *expanded))
.collect();
expanded.sort();
Saved {
show: fold.show,
expanded,
}
}
pub fn apply(self, fold: &mut Fold) {
fold.show = self.show;
fold.expanded = self.expanded.into_iter().collect();
}
}
pub fn path(state_dir: &Path) -> PathBuf {
state_dir.join("tui.json")
}
pub fn load(path: &Path) -> Saved {
std::fs::read(path)
.ok()
.and_then(|bytes| serde_json::from_slice(&bytes).ok())
.unwrap_or_default()
}
pub fn store(path: &Path, saved: &Saved) -> std::io::Result<()> {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir)?;
}
let temporary = path.with_extension("json.tmp");
std::fs::write(&temporary, serde_json::to_vec_pretty(saved)?)?;
std::fs::rename(temporary, path)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tui::fixtures::sample;
use crate::tui::tree;
#[test]
fn round_trips_and_forgets_nodes_that_are_gone() {
let dir = tempfile::tempdir().unwrap();
let file = path(dir.path());
assert_eq!(load(&file), Saved::default(), "nothing saved yet");
let roots = tree::build(&sample());
let mut fold = Fold {
show: Show::Running,
..Fold::default()
};
let stack = NodeId::Stack {
project: "nova".into(),
stack: "main".into(),
};
let gone = NodeId::Project {
project: "gone".into(),
};
fold.expanded.insert(stack.clone(), false);
fold.expanded.insert(gone, true);
store(&file, &Saved::from_fold(&fold, &roots)).unwrap();
let mut restored = Fold::default();
load(&file).apply(&mut restored);
assert_eq!(restored.show, Show::Running);
assert_eq!(
restored.expanded.into_iter().collect::<Vec<_>>(),
[(stack, false)]
);
}
#[test]
fn a_broken_file_means_defaults() {
let dir = tempfile::tempdir().unwrap();
let file = path(dir.path());
std::fs::write(&file, "{ not json").unwrap();
assert_eq!(load(&file), Saved::default());
}
}