pub mod checks;
pub mod convert;
pub mod dto;
#[cfg(test)]
mod fixtures;
pub mod format;
mod hull_base;
mod list;
pub mod naming;
mod open;
mod save;
pub mod staleness;
pub mod store;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_checks;
#[cfg(test)]
mod tests_db;
#[cfg(test)]
mod tests_mesh;
#[cfg(test)]
mod tests_save;
#[cfg(test)]
mod tests_shape;
mod transfer;
use self::dto::DesignShape;
use super::host::{Host, on_host};
use crate::RoughPlanModel;
use indicatrix_vault::db::sqlite::Database;
use slint::ComponentHandle;
use std::{
any::Any,
collections::BTreeMap,
panic::{AssertUnwindSafe, catch_unwind},
rc::Rc,
sync::{Arc, Mutex},
};
use tracing::warn;
struct LoadedFingerprints {
plan_id: i64,
library_id: Option<u32>,
shapes: BTreeMap<i64, DesignShape>,
}
struct PendingBanner {
plan_id: i64,
text: String,
}
#[derive(Default)]
pub(super) struct SavedState {
list_seq: u64,
open_seq: u64,
selected_only: bool,
loaded: Option<LoadedFingerprints>,
pending_banner: Option<PendingBanner>,
}
pub(super) fn setup_saved_callbacks(host: &Rc<Host>) {
let model = host.window.global::<RoughPlanModel>();
model.on_begin_save(|selected_only| on_host(|host| save::begin_save(host, selected_only)));
model.on_confirm_save(|| on_host(save::confirm_save));
model.on_open_saved(|id| on_host(|host| open::open_saved(host, i64::from(id))));
model.on_rename_saved(|id, name| {
on_host(|host| list::rename_saved(host, i64::from(id), &name));
});
model.on_delete_saved(|id| on_host(|host| list::delete_saved(host, i64::from(id))));
model.on_export_saved(|id| on_host(|host| transfer::export_saved(host, i64::from(id))));
model.on_import_saved(|| on_host(transfer::import_saved));
list::refresh_list(host);
}
pub(in crate::gui::rough_plan) fn panic_message(payload: &(dyn Any + Send)) -> String {
payload
.downcast_ref::<&str>()
.map(|message| (*message).to_string())
.or_else(|| payload.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "unknown panic".to_string())
}
fn spawn_task<T: Send + 'static>(
host: &Host,
work: impl FnOnce(&Mutex<Database>) -> Result<T, String> + Send + 'static,
done: impl FnOnce(&Rc<Host>, Result<T, String>) + Send + 'static,
) {
let db = Arc::clone(&host.db);
let weak = host.window.as_weak();
let spawned = std::thread::Builder::new()
.name("rough-saved".to_string())
.spawn(move || {
let value = catch_unwind(AssertUnwindSafe(|| work(&db))).unwrap_or_else(|payload| {
let message = panic_message(&*payload);
warn!("Rough planner: a saved-plan task panicked: {message}");
Err(format!("Internal error: {message}"))
});
let _ = weak.upgrade_in_event_loop(move |_window| {
on_host(|host| done(host, value));
});
});
if let Err(error) = spawned {
warn!("Rough planner: could not start a background task: {error}");
host.window
.global::<RoughPlanModel>()
.set_error_text(format!("Could not start a background task: {error}").into());
}
}
pub(in crate::gui::rough_plan) fn show_error(host: &Host, message: &str) {
host.window
.global::<RoughPlanModel>()
.set_error_text(message.into());
}
pub(in crate::gui::rough_plan) fn show_status(host: &Host, message: &str) {
let model = host.window.global::<RoughPlanModel>();
model.set_status_text(message.into());
model.set_status_level(0);
}
fn show_warning(host: &Host, message: &str) {
let model = host.window.global::<RoughPlanModel>();
model.set_status_text(message.into());
model.set_status_level(1);
}
pub(in crate::gui::rough_plan) fn announce(host: &Host, message: &str) {
if let Some(main) = host.main.upgrade() {
crate::gui::show_toast(&main, message, "success");
}
show_status(host, message);
}