use super::ResourceTab;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DrillItem {
pub tab: ResourceTab,
pub id: String,
pub kind: String,
pub name: String,
pub detail: String
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DrillView {
pub title: String,
pub items: Vec<DrillItem>,
pub selected: usize
}
impl super::App {
pub fn select_project_drill(&mut self, index: usize) {
let Some(project) = self.projects.get(index) else {
return;
};
self.drill = self.drill_cache.get(&project.id).cloned();
self.drill_fetching_id = Some(project.id);
self.drill_retried = None;
self.drill_request = Some((ResourceTab::Projects, project.id, project.name.clone()));
}
#[must_use]
pub const fn take_drill_request(&mut self) -> Option<(ResourceTab, i32, String)> {
self.drill_request.take()
}
pub fn apply_drill(&mut self, id: i32, view: DrillView) {
if self.drill_retried != Some(id)
&& let Some(cached) = self.drill_cache.get(&id)
&& looks_like_flap(cached, &view)
{
self.drill_retried = Some(id);
self.drill_request = Some((ResourceTab::Projects, id, view.title));
return;
}
self.drill_retried = None;
self.drill_cache.insert(id, view.clone());
if self.drill_fetching_id != Some(id) {
return;
}
let selected = self
.drill
.as_ref()
.map_or(0, |d| d.selected)
.min(view.items.len().saturating_sub(1));
self.drill = Some(DrillView {
selected,
..view
});
}
pub fn close_drill(&mut self) {
self.drill = None;
self.drill_fetching_id = None;
self.detail_open = false;
}
pub fn open_drill_item_detail(&mut self) -> bool {
let Some(item) = self
.drill
.as_ref()
.and_then(|d| d.items.get(d.selected))
.cloned()
else {
return false;
};
let index = match item.tab {
ResourceTab::Servers => position(&self.servers, |s| s.id.to_string() == item.id),
ResourceTab::Databases => position(&self.databases, |d| d.id.to_string() == item.id),
ResourceTab::S3 => position(&self.s3_storages, |s| s.id.to_string() == item.id),
ResourceTab::Kubernetes => {
position(&self.k8s_clusters, |c| c.id.to_string() == item.id)
}
ResourceTab::Balancers => position(&self.balancers, |b| b.id.to_string() == item.id),
ResourceTab::DedicatedServers => {
position(&self.dedicated_servers, |d| d.id.to_string() == item.id)
}
ResourceTab::Apps => position(&self.apps, |a| a.id.to_string() == item.id),
_ => None
};
let Some(index) = index else {
return false;
};
self.active_tab = item.tab;
self.filter.clear();
self.filter_editing = false;
self.selected = index;
self.detail_scroll = 0;
self.detail_selected = 0;
self.detail_open = true;
if let Ok(id) = item.id.parse::<i32>() {
self.detail_fetch = Some((item.tab, id));
}
true
}
#[must_use]
pub const fn take_detail_action(
&mut self
) -> Option<(i32, crate::tui::widgets::details::DetailAction)> {
self.detail_action.take()
}
#[must_use]
pub const fn take_detail_fetch(&mut self) -> Option<(ResourceTab, i32)> {
self.detail_fetch.take()
}
#[must_use]
pub const fn drill_open(&self) -> bool {
self.drill.is_some()
}
#[must_use]
pub const fn drill_view(&self) -> Option<&DrillView> {
self.drill.as_ref()
}
}
fn position<T>(items: &[T], pred: impl Fn(&T) -> bool) -> Option<usize> {
items.iter().position(pred)
}
fn looks_like_flap(cached: &DrillView, fresh: &DrillView) -> bool {
fresh.items.len() < cached.items.len()
&& fresh
.items
.iter()
.all(|f| cached.items.iter().any(|c| c.tab == f.tab && c.id == f.id))
}