use std::path::{Path, PathBuf};
use bevy_ecs::change_detection::DetectChangesMut;
use bevy_ecs::prelude::{Commands, Res, ResMut};
use retiretui_engine::params::TaxTables;
use super::{Compared, plans};
use crate::commands::tui::command::Outcome;
use crate::commands::tui::documents::{self, Opening};
use crate::commands::tui::journal;
use crate::commands::tui::session::{self, Projected, Session};
use crate::commands::tui::watch::{self, Stamped};
pub(super) struct ComparedDoc {
pub(super) path: PathBuf,
pub(super) projected: Projected,
files: Stamped,
pub(super) failure: Option<String>,
}
impl ComparedDoc {
pub(super) fn read(path: PathBuf, tables: &TaxTables) -> Result<Self, String> {
let (projected, files) = watch::load_projected(&path, tables)
.map_err(|invalid| invalid.headline().to_owned())?;
Ok(Self {
path,
projected,
files,
failure: None,
})
}
fn reread(&mut self, tables: &TaxTables) {
match watch::load_projected(&self.path, tables) {
Ok((projected, files)) => {
self.projected = projected;
self.files = files;
self.failure = None;
}
Err(invalid) => {
watch::restamp(&mut self.files);
let (name, reason) = (session::file_name(&self.path), invalid.reason());
journal::warn(format!("{name} not re-read: {reason}"));
self.failure = Some(reason.to_owned());
}
}
}
}
impl Compared {
pub fn reload(&mut self, tables: &TaxTables) {
for doc in &mut self.docs {
doc.reread(tables);
}
}
fn reread_stale(&mut self, tables: &TaxTables) -> bool {
let mut is_reread = false;
let stale = self.docs.iter_mut();
for doc in stale.filter(|doc| watch::is_stale(&doc.files)) {
doc.reread(tables);
is_reread = true;
}
is_reread
}
#[must_use]
pub fn swapped(mut self, opened: &Path, left: Option<PathBuf>, tables: &TaxTables) -> Self {
let Some(at) = self.docs.iter().position(|doc| doc.path == opened) else {
return self;
};
self.docs.remove(at);
let is_opened_baseline = self.baseline.as_deref() == Some(opened);
let joined = left.and_then(|path| {
let name = session::file_name(&path).into_owned();
ComparedDoc::read(path, tables)
.inspect_err(|reason| journal::warn(format!("{name} not compared: {reason}")))
.ok()
});
self.baseline = match (&joined, self.baseline.take()) {
(_, Some(_)) if is_opened_baseline => None,
(Some(joined), None) => Some(joined.path.clone()),
(_, kept) => kept,
};
if let Some(joined) = joined {
self.docs.insert(at, joined);
}
self
}
pub(super) fn failure(&self) -> Option<String> {
self.docs.iter().find_map(|doc| {
let reason = doc.failure.as_ref()?;
Some(format!(
"{} not re-read: {reason}",
session::file_name(&doc.path)
))
})
}
}
pub(super) fn follow_disk(session: Res<Session>, mut compared: ResMut<Compared>) {
if compared
.bypass_change_detection()
.reread_stale(&session.tables)
{
compared.set_changed();
}
}
pub(crate) fn open_highlighted(
cursor: plans::Cursor,
session: Res<Session>,
compared: Res<Compared>,
mut commands: Commands,
) -> Outcome {
let at = cursor.place().checked_sub(1);
let Some(doc) = at.and_then(|at| compared.docs.get(at)) else {
return Outcome::Refused(format!("{} is open already", session.file_name()));
};
let opening = Opening::swapping(doc.path.clone());
commands.run_system_cached_with(documents::open, opening);
Outcome::Done
}