use crate::{
DiagramItem, LibraryModel, MainWindow, gui::library::detail::clear_current_detail_display,
};
use anyhow::Result;
use indicatrix_net::library::DesignSummary;
use indicatrix_vault::{
db::sqlite::{Database, DisplayFilters, SortOrder},
model::{entry::DiagramListItem, filter::RangeFilter},
};
use slint::{ComponentHandle, ModelRc, VecModel};
use std::sync::{Arc, Mutex};
use tracing::warn;
fn is_local_url(url: &str) -> bool {
url.starts_with("local://")
}
pub(super) fn to_diagram_item(item: &DesignSummary) -> DiagramItem {
DiagramItem {
id: item.entry_id as i32,
title: item.title.clone().into(),
shape: item.shape.clone().unwrap_or_default().into(),
gear: item.index_gear.clone().unwrap_or_default().into(),
facets: item.facets_count.clone().unwrap_or_default().into(),
designer: item.designer_info.clone().unwrap_or_default().into(),
lw_ratio: item.lw_ratio.clone().unwrap_or_default().into(),
ri: item.refractive_index.clone().unwrap_or_default().into(),
ignored: item.ignored,
is_local: is_local_url(&item.url),
tags: ModelRc::new(VecModel::from(Vec::<slint::SharedString>::new())),
}
}
pub struct DiagramListRow {
pub item: DiagramListItem,
pub ignored: bool,
pub tags: Vec<String>,
}
pub struct FetchedDiagramList {
pub rows: Vec<DiagramListRow>,
pub total: usize,
pub matched_count: usize,
pub excluded_for_missing_curves: usize,
pub count_unavailable: bool,
}
pub fn fetch_diagram_list(
db_mutex: &Arc<Mutex<Database>>,
search: &str,
shape_filter: &str,
gear_filter: &str,
range: &RangeFilter,
) -> Result<FetchedDiagramList> {
fetch_diagram_list_with_options(
db_mutex,
search,
shape_filter,
gear_filter,
range,
DisplaySearchOptions::default(),
)
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DisplaySearchOptions<'a> {
pub order: SortOrder,
pub local_only: bool,
pub tag_filter: Option<&'a str>,
pub id_filter: Option<&'a [i64]>,
}
pub fn fetch_diagram_list_with_options(
db_mutex: &Arc<Mutex<Database>>,
search: &str,
shape_filter: &str,
gear_filter: &str,
range: &RangeFilter,
options: DisplaySearchOptions<'_>,
) -> Result<FetchedDiagramList> {
let db = db_mutex
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let result = fetch_diagram_list_from_db(&db, search, shape_filter, gear_filter, range, options);
drop(db);
result
}
fn fetch_diagram_list_from_db(
db: &Database,
search: &str,
shape_filter: &str,
gear_filter: &str,
range: &RangeFilter,
options: DisplaySearchOptions<'_>,
) -> Result<FetchedDiagramList> {
let clean_shape = if shape_filter == "All Shapes" {
"All"
} else {
shape_filter
};
let clean_gear = if gear_filter == "All Gears" {
"All"
} else {
gear_filter
};
let tag_filter_id = options
.tag_filter
.and_then(|name| db.tag_id_by_name(name).ok().flatten());
let filters = DisplayFilters {
order: options.order,
local_only: options.local_only,
tag_filter: tag_filter_id,
id_filter: options.id_filter,
};
let (result, matched_count) =
db.search_diagrams_display_with_count(search, clean_shape, clean_gear, range, filters)?;
let mut count_unavailable = false;
let total = db.get_total_count().unwrap_or_else(|e| {
warn!("get_total_count failed: {e}");
count_unavailable = true;
result.items.len()
});
let mut tags_by_entry = db.tags_by_entry().unwrap_or_default();
let rows: Vec<DiagramListRow> = result
.items
.into_iter()
.map(|item| {
let ignored = item.ignored;
let tags = tags_by_entry.remove(&item.id).unwrap_or_default();
DiagramListRow {
item,
ignored,
tags,
}
})
.collect();
Ok(FetchedDiagramList {
rows,
total,
matched_count,
excluded_for_missing_curves: result.excluded_for_missing_curves,
count_unavailable,
})
}
pub(super) fn fetch_diagram_list_with_own_connection(
db_mutex: &Arc<Mutex<Database>>,
search: &str,
shape_filter: &str,
gear_filter: &str,
range: &RangeFilter,
options: DisplaySearchOptions<'_>,
) -> Result<FetchedDiagramList> {
match Database::open_read_only(crate::gui::DB_PATH) {
Ok(read_db) => {
fetch_diagram_list_from_db(&read_db, search, shape_filter, gear_filter, range, options)
}
Err(e) => {
warn!(
"background search: could not open a dedicated read-only connection to \
{:?} ({e}); falling back to the shared database lock for this query",
crate::gui::DB_PATH
);
fetch_diagram_list_with_options(
db_mutex,
search,
shape_filter,
gear_filter,
range,
options,
)
}
}
}
pub fn apply_diagram_list_to_ui(ui: &MainWindow, fetched: FetchedDiagramList) {
let selected_id = ui.global::<LibraryModel>().get_selected_entry_id();
if selected_id >= 0
&& !fetched
.rows
.iter()
.any(|row| row.item.id == i64::from(selected_id))
{
clear_current_detail_display(ui);
}
let count_unavailable = fetched.count_unavailable;
let slint_items: Vec<DiagramItem> = fetched
.rows
.into_iter()
.map(|row| DiagramItem {
id: row.item.id as i32,
title: row.item.title.into(),
shape: row.item.shape.unwrap_or_default().into(),
gear: row.item.index_gear.unwrap_or_default().into(),
facets: row.item.facets_count.unwrap_or_default().into(),
designer: row.item.designer_info.unwrap_or_default().into(),
lw_ratio: row.item.lw_ratio.unwrap_or_default().into(),
ri: row.item.refractive_index.unwrap_or_default().into(),
ignored: row.ignored,
is_local: is_local_url(&row.item.url),
tags: ModelRc::new(VecModel::from(
row.tags
.into_iter()
.map(Into::into)
.collect::<Vec<slint::SharedString>>(),
)),
})
.collect();
ui.global::<LibraryModel>()
.set_diagram_list(ModelRc::new(VecModel::from(slint_items)));
ui.global::<LibraryModel>()
.set_total_count(fetched.total as i32);
ui.global::<LibraryModel>()
.set_matched_count(fetched.matched_count as i32);
ui.global::<LibraryModel>()
.set_performance_excluded_count(fetched.excluded_for_missing_curves as i32);
if count_unavailable {
ui.global::<LibraryModel>()
.set_status_message("Count unavailable.".into());
}
}