use std::time::Instant;
use anyhow::Result;
use crossbeam_channel::Sender;
use super::{Event, Server};
use crate::paths;
use crate::persist::{self, SessionSnapshot};
impl Server {
pub(super) fn persist(&mut self) {
self.persist_dirty = false;
self.last_persist = Instant::now();
if self.workspaces.is_empty() {
return;
}
let snap = persist::snapshot(self);
let _ = self.persister.send(Persist::Save(self.session.clone(), snap));
}
pub fn mark_persist(&mut self) {
self.persist_dirty = true;
}
pub(super) fn restore(&mut self) {
let interrupted = crate::runbooks::recover_interrupted(&self.session);
if !interrupted.is_empty() {
self.warnings.push(format!(
"kraal stopped while these runbooks ran: {} (their logs say how far they got)",
interrupted.join(", ")
));
}
let snap = match persist::load(&self.session) {
Ok(Some(s)) => s,
Ok(None) => return,
Err(e) => {
let path = paths::session_state_path(&self.session);
let aside = path.with_extension(format!("json.unreadable-{}", crate::accounts::now()));
let _ = std::fs::rename(&path, &aside);
self.warnings.push(format!(
"could not restore the previous session ({e:#}); kept it as {}",
paths::shorten_home(&aside)
));
return;
}
};
if let Err(e) = self.apply_snapshot(snap) {
self.warnings.push(format!("restore: {e:#}"));
}
}
pub(super) fn apply_snapshot(&mut self, snap: SessionSnapshot) -> Result<()> {
persist::apply(self, snap)
}
}
pub(super) enum Persist {
Save(String, SessionSnapshot),
Delete(String),
Stop(Sender<Result<(), String>>),
}
pub(super) fn spawn_persister(events: Sender<Event>) -> Sender<Persist> {
let (tx, rx) = crossbeam_channel::unbounded::<Persist>();
let _ = std::thread::Builder::new().name("persist".into()).spawn(move || {
let mut last: Result<(), String> = Ok(());
while let Ok(first) = rx.recv() {
let mut queue = vec![first];
queue.extend(rx.try_iter());
for (i, job) in queue.iter().enumerate() {
match job {
Persist::Save(..) if matches!(queue.get(i + 1), Some(Persist::Save(..))) => {}
Persist::Save(session, snap) => {
last = persist::save(session, snap).map_err(|e| format!("{e:#}"));
if let Err(e) = &last {
let _ = events.send(Event::Warning(format!("saving session state: {e}")));
}
}
Persist::Delete(session) => {
let _ = std::fs::remove_file(paths::session_state_path(session));
last = Ok(());
}
Persist::Stop(done) => {
let _ = done.send(last.clone());
return;
}
}
}
}
});
tx
}