|
|
@ -1,5 +1,11 @@ |
|
|
|
use std::str::FromStr; |
|
|
|
use super::impls::CRUD; |
|
|
|
|
|
|
|
use anyhow::Result as AResult; |
|
|
|
|
|
|
|
use std::{fmt::Display, str::FromStr}; |
|
|
|
use strum::VariantNames; |
|
|
|
use strum::VariantNames; |
|
|
|
|
|
|
|
use tokio::join; |
|
|
|
|
|
|
|
use tui::widgets::ListState; |
|
|
|
|
|
|
|
use u_lib::models::{Agent, AssignedJob, JobMeta}; |
|
|
|
|
|
|
|
use uuid::Uuid; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(strum::Display, strum::EnumVariantNames, strum::EnumString)] |
|
|
|
#[derive(strum::Display, strum::EnumVariantNames, strum::EnumString)] |
|
|
|
pub enum UiTabs { |
|
|
|
pub enum UiTabs { |
|
|
@ -30,19 +36,73 @@ impl UiTabs { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct StatefulList<T: CRUD> { |
|
|
|
|
|
|
|
pub updated: bool, |
|
|
|
|
|
|
|
pub inner: Vec<T>, |
|
|
|
|
|
|
|
pub state: ListState, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: CRUD> StatefulList<T> { |
|
|
|
|
|
|
|
pub async fn update(&mut self) -> AResult<()> { |
|
|
|
|
|
|
|
if !self.updated { |
|
|
|
|
|
|
|
let new_values = <T as CRUD>::read().await?; |
|
|
|
|
|
|
|
self.inner = new_values; |
|
|
|
|
|
|
|
self.updated = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn delete(&mut self) -> AResult<()> { |
|
|
|
|
|
|
|
if let Some(s) = self.state.selected() { |
|
|
|
|
|
|
|
let uid = self.inner[s].id(); |
|
|
|
|
|
|
|
<T as CRUD>::delete(uid).await?; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn get(&self, id: Uuid) -> Option<&T> { |
|
|
|
|
|
|
|
for item in self.inner.iter() { |
|
|
|
|
|
|
|
if item.id() == id { |
|
|
|
|
|
|
|
return Some(item); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
None |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: CRUD> Default for StatefulList<T> { |
|
|
|
|
|
|
|
fn default() -> Self { |
|
|
|
|
|
|
|
let mut state = ListState::default(); |
|
|
|
|
|
|
|
state.select(Some(0)); |
|
|
|
|
|
|
|
StatefulList { |
|
|
|
|
|
|
|
updated: false, |
|
|
|
|
|
|
|
inner: vec![], |
|
|
|
|
|
|
|
state, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct State { |
|
|
|
pub struct State { |
|
|
|
pub token: String, |
|
|
|
|
|
|
|
pub active_tab: UiTabs, |
|
|
|
pub active_tab: UiTabs, |
|
|
|
|
|
|
|
pub last_error: Option<String>, |
|
|
|
|
|
|
|
pub agents: StatefulList<Agent>, |
|
|
|
|
|
|
|
pub jobs: StatefulList<JobMeta>, |
|
|
|
|
|
|
|
pub map: StatefulList<AssignedJob>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl State { |
|
|
|
impl Default for State { |
|
|
|
pub fn new(token: String) -> Self { |
|
|
|
fn default() -> Self { |
|
|
|
State { |
|
|
|
State { |
|
|
|
token, |
|
|
|
|
|
|
|
active_tab: UiTabs::Agents, |
|
|
|
active_tab: UiTabs::Agents, |
|
|
|
|
|
|
|
last_error: None, |
|
|
|
|
|
|
|
agents: Default::default(), |
|
|
|
|
|
|
|
jobs: Default::default(), |
|
|
|
|
|
|
|
map: Default::default(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl State { |
|
|
|
pub fn next_tab(&mut self) { |
|
|
|
pub fn next_tab(&mut self) { |
|
|
|
self.active_tab = self.active_tab.next() |
|
|
|
self.active_tab = self.active_tab.next() |
|
|
|
} |
|
|
|
} |
|
|
@ -50,4 +110,129 @@ impl State { |
|
|
|
pub fn prev_tab(&mut self) { |
|
|
|
pub fn prev_tab(&mut self) { |
|
|
|
self.active_tab = self.active_tab.prev() |
|
|
|
self.active_tab = self.active_tab.prev() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn check_err(&mut self, res: AResult<()>) -> bool { |
|
|
|
|
|
|
|
if let Err(e) = res { |
|
|
|
|
|
|
|
self.last_error = Some(e.to_string()); |
|
|
|
|
|
|
|
true |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn check_updates(&mut self) { |
|
|
|
|
|
|
|
if !self.agents.updated || !self.jobs.updated || !self.map.updated { |
|
|
|
|
|
|
|
let state = self.tab_list_state(); |
|
|
|
|
|
|
|
if let None = state.selected() { |
|
|
|
|
|
|
|
state.select(Some(0)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let (a, j, m) = join! { |
|
|
|
|
|
|
|
self.agents.update(), |
|
|
|
|
|
|
|
self.jobs.update(), |
|
|
|
|
|
|
|
self.map.update() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
for res in [a, j, m] { |
|
|
|
|
|
|
|
self.check_err(res); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn tab_data(&self) -> Vec<String> { |
|
|
|
|
|
|
|
match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => self |
|
|
|
|
|
|
|
.agents |
|
|
|
|
|
|
|
.inner |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.map(|i| format!("{}: {}-{}", crop(i.id, 6), i.username, i.hostname)) |
|
|
|
|
|
|
|
.collect(), |
|
|
|
|
|
|
|
UiTabs::Jobs => self |
|
|
|
|
|
|
|
.jobs |
|
|
|
|
|
|
|
.inner |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.map(|i| format!("{}: {}", crop(i.id, 6), i.alias.clone().unwrap_or_default())) |
|
|
|
|
|
|
|
.collect(), |
|
|
|
|
|
|
|
UiTabs::Map => self |
|
|
|
|
|
|
|
.map |
|
|
|
|
|
|
|
.inner |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.map(|i| { |
|
|
|
|
|
|
|
let job = self.jobs.get(i.job_id).unwrap(); |
|
|
|
|
|
|
|
let job_id = crop(i.job_id, 6); |
|
|
|
|
|
|
|
let job_ident = if let Some(alias) = job.alias.as_ref() { |
|
|
|
|
|
|
|
format!("{} ({})", alias, job_id) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
format!("{}", job_id) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let agent = self.agents.get(i.agent_id).unwrap(); |
|
|
|
|
|
|
|
let agent_id = crop(i.agent_id, 6); |
|
|
|
|
|
|
|
let agent_ident = if let Some(alias) = agent.alias.as_ref() { |
|
|
|
|
|
|
|
format!("{} ({})", alias, agent_id) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
format!("{}-{} ({})", agent.username, agent.hostname, agent_id) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
format!("{}: {} for {}", crop(i.id, 6), job_ident, agent_ident) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect(), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn tab_list_state(&mut self) -> &mut ListState { |
|
|
|
|
|
|
|
match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => &mut self.agents.state, |
|
|
|
|
|
|
|
UiTabs::Jobs => &mut self.jobs.state, |
|
|
|
|
|
|
|
UiTabs::Map => &mut self.map.state, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn update_tab(&mut self) { |
|
|
|
|
|
|
|
match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => self.agents.updated = false, |
|
|
|
|
|
|
|
UiTabs::Jobs => self.jobs.updated = false, |
|
|
|
|
|
|
|
UiTabs::Map => self.map.updated = false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn on_down(&mut self) { |
|
|
|
|
|
|
|
let (list_len, list_state) = match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => (self.agents.inner.len(), &mut self.agents.state), |
|
|
|
|
|
|
|
UiTabs::Jobs => (self.jobs.inner.len(), &mut self.jobs.state), |
|
|
|
|
|
|
|
UiTabs::Map => (self.map.inner.len(), &mut self.map.state), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if list_len == 0 { |
|
|
|
|
|
|
|
list_state.select(None); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
let selected = list_state.selected().unwrap_or(0); |
|
|
|
|
|
|
|
list_state.select(Some((selected + 1) % list_len)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn on_up(&mut self) { |
|
|
|
|
|
|
|
let (list_len, list_state) = match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => (self.agents.inner.len(), &mut self.agents.state), |
|
|
|
|
|
|
|
UiTabs::Jobs => (self.jobs.inner.len(), &mut self.jobs.state), |
|
|
|
|
|
|
|
UiTabs::Map => (self.map.inner.len(), &mut self.map.state), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if list_len == 0 { |
|
|
|
|
|
|
|
list_state.select(None); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
let selected = list_state.selected().unwrap_or(1); |
|
|
|
|
|
|
|
list_state.select(Some((selected + list_len - 1) % list_len)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn delete(&mut self) { |
|
|
|
|
|
|
|
let res = match self.active_tab { |
|
|
|
|
|
|
|
UiTabs::Agents => self.agents.delete().await, |
|
|
|
|
|
|
|
UiTabs::Jobs => self.jobs.delete().await, |
|
|
|
|
|
|
|
UiTabs::Map => self.map.delete().await, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if !self.check_err(res) { |
|
|
|
|
|
|
|
self.on_up(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn crop<T: Display>(data: T, retain: usize) -> String { |
|
|
|
|
|
|
|
data.to_string()[..retain].to_string() |
|
|
|
} |
|
|
|
} |
|
|
|