biiiig tui, but still not working

pull/1/head
plazmoid 3 years ago
parent 638ae4da6e
commit bda30e2a72
  1. 12
      .gitignore
  2. 2
      bin/u_agent/src/lib.rs
  3. 1
      bin/u_panel/Cargo.toml
  4. 18
      bin/u_panel/src/argparse.rs
  5. 8
      bin/u_panel/src/main.rs
  6. 18
      bin/u_panel/src/tui/impls.rs
  7. 54
      bin/u_panel/src/tui/mod.rs
  8. 42
      bin/u_panel/src/tui/retval.rs
  9. 31
      bin/u_panel/src/tui/utils.rs
  10. 133
      bin/u_panel/src/tui/windows/confirm.rs
  11. 50
      bin/u_panel/src/tui/windows/main_wnd.rs
  12. 199
      bin/u_panel/src/tui/windows/mod.rs
  13. 0
      bin/u_panel/src/tui/windows/processing.rs
  14. 4
      integration/tests/behaviour.rs
  15. 6
      integration/tests/helpers/panel.rs
  16. 4
      lib/u_lib/src/builder.rs
  17. 16
      lib/u_lib/src/cache.rs
  18. 2
      lib/u_lib/src/models/jobs/assigned.rs

12
.gitignore vendored

@ -1,12 +1,14 @@
target/ target/
**/*.rs.bk
.idea/ .idea/
data/ data/
certs/
static/
.vscode/
release/
**/*.rs.bk
**/*.pyc **/*.pyc
certs/*
*.log *.log
echoer echoer
.env.private .env.private
*.lock *.lock
static/
.vscode/

@ -29,7 +29,7 @@ const ITERATION_LATENCY: u64 = 5;
pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHandler) { pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHandler) {
if !job_requests.is_empty() { if !job_requests.is_empty() {
for jr in &job_requests { for jr in &job_requests {
if !JobCache::contains(&jr.job_id) { if !JobCache::contains(jr.job_id) {
debug!("Fetching job: {}", &jr.job_id); debug!("Fetching job: {}", &jr.job_id);
let fetched_job = loop { let fetched_job = loop {
match client.get_jobs(Some(jr.job_id)).await { match client.get_jobs(Some(jr.job_id)).await {

@ -23,3 +23,4 @@ anyhow = "1.0.44"
strum = { version = "0.22.0", features = ["derive"] } strum = { version = "0.22.0", features = ["derive"] }
async-trait = "0.1.51" async-trait = "0.1.51"
once_cell = "1.8.0" once_cell = "1.8.0"
crossbeam = "0.8.1"

@ -1,10 +1,7 @@
use once_cell::sync::Lazy; use crate::CLIENT;
use std::env;
use std::fmt; use std::fmt;
use structopt::StructOpt; use structopt::StructOpt;
use u_lib::{ use u_lib::{datatypes::DataResult, messaging::AsMsg, models::JobMeta, UError, UResult};
api::ClientHandler, datatypes::DataResult, messaging::AsMsg, models::JobMeta, UError, UResult,
};
use uuid::Uuid; use uuid::Uuid;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
@ -26,9 +23,8 @@ enum Cmd {
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
enum JobALD { enum JobALD {
Add { Add {
#[structopt(long, parse(try_from_str = parse_uuid))] //#[structopt(long, parse(try_from_str = parse_uuid))]
agent: Option<Uuid>, //agent: Option<Uuid>,
#[structopt(long)] #[structopt(long)]
alias: String, alias: String,
@ -75,11 +71,6 @@ enum LD {
}, },
} }
pub static CLIENT: Lazy<ClientHandler> = Lazy::new(|| {
let token = env::var("ADMIN_AUTH_TOKEN").expect("access token is not set");
ClientHandler::new(None).password(token.clone())
});
fn parse_uuid(src: &str) -> Result<Uuid, String> { fn parse_uuid(src: &str) -> Result<Uuid, String> {
Uuid::parse_str(src).map_err(|e| e.to_string()) Uuid::parse_str(src).map_err(|e| e.to_string())
} }
@ -135,7 +126,6 @@ pub async fn process_cmd(args: Args) -> UResult<()> {
JobMapALD::List { uid } => printer.print(CLIENT.get_agent_jobs(uid).await), JobMapALD::List { uid } => printer.print(CLIENT.get_agent_jobs(uid).await),
JobMapALD::Delete { uid } => printer.print(CLIENT.del(Some(uid)).await), JobMapALD::Delete { uid } => printer.print(CLIENT.del(Some(uid)).await),
}, },
//Cmd::Server => be::serve().unwrap(),
Cmd::TUI => crate::tui::init_tui() Cmd::TUI => crate::tui::init_tui()
.await .await
.map_err(|e| UError::TUIError(e.to_string()))?, .map_err(|e| UError::TUIError(e.to_string()))?,

@ -5,10 +5,18 @@ mod tui;
extern crate async_trait; extern crate async_trait;
use argparse::{process_cmd, Args}; use argparse::{process_cmd, Args};
use once_cell::sync::Lazy;
use std::env;
use std::process; use std::process;
use structopt::StructOpt; use structopt::StructOpt;
use u_lib::api::ClientHandler;
use u_lib::utils::init_env; use u_lib::utils::init_env;
pub static CLIENT: Lazy<ClientHandler> = Lazy::new(|| {
let token = env::var("ADMIN_AUTH_TOKEN").expect("access token is not set");
ClientHandler::new(None).password(token.clone())
});
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
init_env(); init_env();

@ -1,28 +1,30 @@
use crate::argparse::CLIENT; use crate::tui::windows::{ConfirmWnd, MainWnd, WndId};
use crate::CLIENT;
use u_lib::models::{Agent, AssignedJob, JobMeta}; use u_lib::models::{Agent, AssignedJob, JobMeta};
use u_lib::UResult; use u_lib::UResult;
use uuid::Uuid; use uuid::Uuid;
pub trait Id { pub trait Id<T> {
fn id(&self) -> Uuid; fn id(&self) -> T;
} }
#[macro_export] #[macro_export]
macro_rules! impl_id { macro_rules! impl_id {
($($type:ty),+) => { ($id_type:tt => $($type:tt),+) => {
$( $(
impl Id for $type { impl Id<$id_type> for $type {
fn id(&self) -> Uuid { fn id(&self) -> $id_type {
self.id self.id
} }
})+ })+
}; };
} }
impl_id!(Agent, JobMeta, AssignedJob); impl_id!(Uuid => Agent, JobMeta, AssignedJob);
impl_id!(WndId => MainWnd, ConfirmWnd);
#[async_trait] #[async_trait]
pub trait CRUD: Id pub trait CRUD: Id<Uuid>
where where
Self: Sized, Self: Sized,
{ {

@ -1,29 +1,39 @@
mod impls; mod impls;
mod retval;
mod utils;
mod windows; mod windows;
use anyhow::Result as AResult; use anyhow::Result as AResult;
use backtrace::Backtrace; use backtrace::Backtrace;
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event}; use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode};
use crossterm::execute; use crossterm::execute;
use crossterm::terminal::{ use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
}; };
use once_cell::sync::Lazy;
use retval::{RetVal, ReturnValue};
use std::panic::set_hook; use std::panic::set_hook;
use std::process::exit; use std::process::exit;
use std::{ use std::{
io::{stdout, Stdout}, io::{stdout, Stdout},
sync::mpsc,
thread, thread,
time::Duration, time::Duration,
}; };
use tui::{backend::CrosstermBackend, Terminal}; use tui::{backend::CrosstermBackend, Terminal};
use windows::{MainWnd, WindowsHandler}; use utils::Channel;
use windows::{MainWnd, SharedWnd, WindowsHandler, WndId};
pub type Backend = CrosstermBackend<Stdout>; pub type Backend = CrosstermBackend<Stdout>;
pub type Frame<'f> = tui::Frame<'f, Backend>; pub type Frame<'f> = tui::Frame<'f, Backend>;
enum InputEvent<I> { const EVENT_GEN_PERIOD: Duration = Duration::from_millis(120);
Key(I),
static GENERAL_EVENT_CHANNEL: Lazy<Channel<GEvent>> = Lazy::new(|| Channel::new());
enum GEvent {
CreateWnd(SharedWnd),
CloseWnd { wid: WndId, force: bool },
Key(KeyCode),
Tick, Tick,
} }
@ -51,32 +61,38 @@ async fn init() -> AResult<()> {
enable_raw_mode()?; enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?; execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
let mut wh = WindowsHandler::new(get_terminal()?); WindowsHandler::lock().await.push_new::<MainWnd>().await;
wh.push::<MainWnd>();
let (tx, rx) = mpsc::channel();
thread::spawn(move || loop { thread::spawn(move || loop {
if event::poll(Duration::from_millis(10)).unwrap() { if event::poll(EVENT_GEN_PERIOD).unwrap() {
match event::read().unwrap() { match event::read().unwrap() {
key @ Event::Key(_) => tx.send(InputEvent::Key(key)).unwrap(), Event::Key(key) => GENERAL_EVENT_CHANNEL.send(GEvent::Key(key.code)),
_ => (), _ => (),
} }
} else { } else {
tx.send(InputEvent::Tick).unwrap() GENERAL_EVENT_CHANNEL.send(GEvent::Tick)
} }
}); });
wh.clear()?; WindowsHandler::lock().await.clear()?;
loop { loop {
wh.draw()?; match GENERAL_EVENT_CHANNEL.recv() {
wh.update().await?; GEvent::Tick => {
match rx.recv()? { let mut wh = WindowsHandler::lock().await;
InputEvent::Key(Event::Key(key)) => { wh.update().await?;
wh.handle_kbd(key.code).await?; wh.draw().await?;
}
GEvent::CloseWnd { wid, force } => {
let mut wh = WindowsHandler::lock().await;
wh.close(wid, force).await;
}
GEvent::Key(key) => {
WindowsHandler::lock().await.send_handle_kbd(key);
}
GEvent::CreateWnd(wnd) => {
WindowsHandler::lock().await.push_dyn(wnd).await;
} }
InputEvent::Tick => (),
_ => unreachable!(),
} }
} }
} }

@ -0,0 +1,42 @@
use once_cell::sync::Lazy;
use std::any::{type_name, Any};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::Mutex;
type RV = Box<dyn Any + Send>;
static LAST_RETVAL: Lazy<Mutex<Option<RV>>> = Lazy::new(|| Mutex::new(None));
pub struct ReturnValue;
impl ReturnValue {
pub async fn set(t: RV) {
*LAST_RETVAL.lock().await = Some(t);
}
pub async fn get<T: 'static>() -> Box<T> {
ReturnValue
.await
.downcast::<T>()
.expect(&format!("wrong type {}", type_name::<T>()))
}
}
impl Future for ReturnValue {
type Output = Box<dyn Any>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Poll::Ready(mut rv) = unsafe { Pin::new_unchecked(&mut LAST_RETVAL.lock()) }.poll(cx)
{
if let Some(rv) = rv.take() {
return Poll::Ready(rv);
}
}
Poll::Pending
}
}
pub trait RetVal {
fn retval(&self) -> RV;
}

@ -0,0 +1,31 @@
use crossbeam::channel::{unbounded, Receiver, Sender};
pub struct Channel<T> {
pub tx: Sender<T>,
pub rx: Receiver<T>,
}
impl<T> Channel<T> {
pub fn new() -> Self {
let (tx, rx) = unbounded::<T>();
Self { tx, rx }
}
pub fn send(&self, msg: T) {
self.tx.send(msg).unwrap()
}
pub fn recv(&self) -> T {
self.rx.recv().unwrap()
}
pub fn is_empty(&self) -> bool {
self.rx.is_empty()
}
}
impl<T> Default for Channel<T> {
fn default() -> Self {
Channel::new()
}
}

@ -0,0 +1,133 @@
use super::{Window, WndId};
use crate::tui::{Frame, GEvent, RetVal, ReturnValue, GENERAL_EVENT_CHANNEL};
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use std::any::Any;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Modifier, Style};
use tui::widgets::{Block, Clear, List, ListItem, ListState, Paragraph};
#[derive(Default)]
pub struct ConfirmWnd {
pub id: WndId,
msg: String,
variants: Vec<String>,
state: ListState,
}
impl ConfirmWnd {
pub fn new_yn(msg: impl Into<String>, variants: Option<Vec<String>>) -> Self {
let default = vec!["Yes".to_string(), "No".to_string()];
let variants = match variants {
Some(v) if !v.is_empty() => v,
_ => default,
};
let mut this = Self {
msg: msg.into(),
variants,
..Default::default()
};
this.state.select(Some(0));
this
}
pub fn on_right(&mut self) {
let selected = self.state.selected().unwrap_or(0);
self.state
.select(Some((selected + 1).rem_euclid(self.variants.len())));
}
pub fn on_left(&mut self) {
let selected = self.state.selected().unwrap_or(0);
let vars_len = self.variants.len();
self.state
.select(Some((selected + vars_len - 1).rem_euclid(vars_len)));
}
}
impl RetVal for ConfirmWnd {
fn retval(&self) -> Box<dyn Any + Send> {
let value = self
.variants
.get(self.state.selected().unwrap())
.unwrap()
.to_owned();
Box::new(value)
}
}
#[async_trait]
impl Window for ConfirmWnd {
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
match k {
KeyCode::Right => self.on_right(),
KeyCode::Left => self.on_left(),
KeyCode::Enter | KeyCode::Esc => self.close(false).await,
_ => (),
}
Ok(())
}
async fn handle_update(&mut self) -> AResult<()> {
Ok(())
}
async fn handle_close(&mut self) -> bool {
true
}
fn draw(&mut self, f: &mut Frame) {
let size = f.size();
let rect = centered_rect(60, 40, size);
f.render_widget(Clear, rect);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)])
.split(rect);
let msg = Paragraph::new(self.msg.as_ref());
f.render_widget(msg, chunks[0]);
let options = self
.variants
.iter()
.map(AsRef::as_ref)
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list =
List::new(options).highlight_style(Style::default().add_modifier(Modifier::BOLD));
f.render_stateful_widget(list, chunks[1], &mut self.state);
}
}
pub async fn confirm_wnd(msg: impl Into<String>) -> bool {
let wnd = ConfirmWnd::new_yn(msg.into(), None);
GENERAL_EVENT_CHANNEL.send(GEvent::CreateWnd(wnd.into_shared()));
*ReturnValue::get().await
}
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.split(r);
Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.split(popup_layout[1])[1]
}

@ -1,7 +1,8 @@
use super::Window; use super::{confirm_wnd, Window};
use crate::tui::{impls::CRUD, Frame}; use crate::tui::{impls::CRUD, windows::WndId, Frame, RetVal};
use anyhow::Result as AResult; use anyhow::Result as AResult;
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use std::any::Any;
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
use strum::VariantNames; use strum::VariantNames;
use tokio::join; use tokio::join;
@ -32,13 +33,13 @@ impl UiTabs {
} }
pub fn next(&self) -> Self { pub fn next(&self) -> Self {
let next_idx = (self.index() + 1) % Self::VARIANTS.len(); let next_idx = (self.index() + 1).rem_euclid(Self::VARIANTS.len());
Self::from_str(Self::VARIANTS[next_idx]).unwrap() Self::from_str(Self::VARIANTS[next_idx]).unwrap()
} }
pub fn prev(&self) -> Self { pub fn prev(&self) -> Self {
let vlen = Self::VARIANTS.len(); let vlen = Self::VARIANTS.len();
let next_idx = (self.index() + vlen - 1) % vlen; let next_idx = (self.index() + vlen - 1).rem_euclid(vlen);
Self::from_str(Self::VARIANTS[next_idx]).unwrap() Self::from_str(Self::VARIANTS[next_idx]).unwrap()
} }
} }
@ -52,7 +53,7 @@ pub struct StatefulList<T: CRUD> {
impl<T: CRUD> StatefulList<T> { impl<T: CRUD> StatefulList<T> {
pub async fn update(&mut self) -> AResult<()> { pub async fn update(&mut self) -> AResult<()> {
if !self.updated { if !self.updated {
let new_values = <T as CRUD>::read().await?; let new_values = T::read().await?;
self.inner = new_values; self.inner = new_values;
self.updated = true; self.updated = true;
} }
@ -62,7 +63,7 @@ impl<T: CRUD> StatefulList<T> {
pub async fn delete(&mut self) -> AResult<()> { pub async fn delete(&mut self) -> AResult<()> {
if let Some(s) = self.state.selected() { if let Some(s) = self.state.selected() {
let uid = self.inner[s].id(); let uid = self.inner[s].id();
<T as CRUD>::delete(uid).await?; T::delete(uid).await?;
} }
Ok(()) Ok(())
} }
@ -90,6 +91,7 @@ impl<T: CRUD> Default for StatefulList<T> {
} }
pub struct MainWnd { pub struct MainWnd {
pub id: WndId,
pub active_tab: UiTabs, pub active_tab: UiTabs,
pub last_error: Option<String>, pub last_error: Option<String>,
pub agents: StatefulList<Agent>, pub agents: StatefulList<Agent>,
@ -105,6 +107,7 @@ impl Default for MainWnd {
agents: Default::default(), agents: Default::default(),
jobs: Default::default(), jobs: Default::default(),
map: Default::default(), map: Default::default(),
id: Default::default(),
} }
} }
} }
@ -194,7 +197,10 @@ impl MainWnd {
pub fn update_tab(&mut self) { pub fn update_tab(&mut self) {
match self.active_tab { match self.active_tab {
UiTabs::Agents => self.agents.updated = false, UiTabs::Agents => {
self.agents.updated = false;
self.jobs.updated = false;
}
UiTabs::Jobs => self.jobs.updated = false, UiTabs::Jobs => self.jobs.updated = false,
UiTabs::Map => self.map.updated = false, UiTabs::Map => self.map.updated = false,
} }
@ -209,8 +215,8 @@ impl MainWnd {
if list_len == 0 { if list_len == 0 {
list_state.select(None); list_state.select(None);
} else { } else {
let selected = list_state.selected().unwrap_or(0); let selected = list_state.selected().unwrap_or(list_len - 1);
list_state.select(Some((selected + 1) % list_len)); list_state.select(Some((selected + 1).rem_euclid(list_len)));
} }
} }
@ -224,7 +230,7 @@ impl MainWnd {
list_state.select(None); list_state.select(None);
} else { } else {
let selected = list_state.selected().unwrap_or(1); let selected = list_state.selected().unwrap_or(1);
list_state.select(Some((selected + list_len - 1) % list_len)); list_state.select(Some((selected + list_len - 1).rem_euclid(list_len)));
} }
} }
@ -244,18 +250,16 @@ impl MainWnd {
impl Window for MainWnd { impl Window for MainWnd {
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> { async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
match k { match k {
KeyCode::Esc => { KeyCode::Esc => self.close(false).await,
/*teardown()?;
wh.show_cursor()?;
break;*/
}
KeyCode::Left => self.prev_tab(), KeyCode::Left => self.prev_tab(),
KeyCode::Right => self.next_tab(), KeyCode::Right => self.next_tab(),
KeyCode::Up => self.on_up(), KeyCode::Up => self.on_up(),
KeyCode::Down => self.on_down(), KeyCode::Down => self.on_down(),
KeyCode::Delete => { KeyCode::Delete => {
self.delete().await; if confirm_wnd("Delete?").await {
self.update_tab(); self.delete().await;
self.update_tab();
}
} }
KeyCode::F(5) => self.update_tab(), KeyCode::F(5) => self.update_tab(),
_ => (), _ => (),
@ -263,13 +267,13 @@ impl Window for MainWnd {
Ok(()) Ok(())
} }
async fn update(&mut self) -> AResult<()> { async fn handle_update(&mut self) -> AResult<()> {
self.check_updates().await; self.check_updates().await;
Ok(()) Ok(())
} }
async fn close(&mut self) { async fn handle_close(&mut self) -> bool {
//self.show_cursor() true
} }
fn draw(&mut self, f: &mut Frame) { fn draw(&mut self, f: &mut Frame) {
@ -308,6 +312,12 @@ impl Window for MainWnd {
} }
} }
impl RetVal for MainWnd {
fn retval(&self) -> Box<dyn Any + Send> {
Box::new(())
}
}
fn crop<T: Display>(data: T, retain: usize) -> String { fn crop<T: Display>(data: T, retain: usize) -> String {
data.to_string()[..retain].to_string() data.to_string()[..retain].to_string()
} }

@ -1,47 +1,158 @@
mod confirm;
mod main_wnd; mod main_wnd;
mod processing; pub use confirm::{confirm_wnd, ConfirmWnd};
use crossbeam::channel::Sender;
pub use main_wnd::MainWnd; pub use main_wnd::MainWnd;
use crate::tui::{Backend, Frame}; use crate::tui::{
get_terminal, impls::Id, teardown, utils::Channel, Backend, Frame, GEvent, RetVal, ReturnValue,
GENERAL_EVENT_CHANNEL,
};
use anyhow::Result as AResult; use anyhow::Result as AResult;
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use std::cell::RefCell; use once_cell::sync::{Lazy, OnceCell};
use std::collections::BTreeMap;
use std::process::exit; use std::process::exit;
use std::rc::Rc; use std::sync::Arc;
use std::sync::{Mutex as StdMutex, MutexGuard as StdMutexGuard};
use tokio::sync::{Mutex, MutexGuard};
use tokio::task::{self, JoinHandle};
use tui::Terminal; use tui::Terminal;
type Wnd = Rc<RefCell<dyn Window>>; static WINDOWS: Lazy<Arc<Mutex<WindowsHandler>>> =
Lazy::new(|| Arc::new(Mutex::new(WindowsHandler::new(get_terminal().unwrap()))));
static LAST_WND_ID: OnceCell<StdMutex<WndId>> = OnceCell::new();
static WND_EVENT_CHANNEL: Lazy<Channel<WndEvent>> = Lazy::new(|| Channel::new());
pub type SharedWnd = Arc<Mutex<dyn Window>>;
enum WndEvent {
Key(KeyCode),
}
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Copy, Clone, Debug)]
pub struct WndId(u64);
impl WndId {
pub fn last_wid() -> StdMutexGuard<'static, WndId> {
LAST_WND_ID
.get_or_init(|| StdMutex::new(WndId(0)))
.lock()
.unwrap()
}
}
impl Default for WndId {
fn default() -> Self {
let mut wid = Self::last_wid();
wid.0 += 1;
*wid
}
}
#[async_trait] #[async_trait]
pub trait Window { pub trait Window: Id<WndId> + RetVal + Send {
async fn check_wnd_events(&mut self) {
if !WND_EVENT_CHANNEL.is_empty() {
match WND_EVENT_CHANNEL.recv() {
WndEvent::Key(k) => {
self.handle_kbd(k).await.unwrap();
}
}
}
}
async fn close(&mut self, force: bool) {
let rv = self.retval();
ReturnValue::set(rv).await;
GENERAL_EVENT_CHANNEL.send(GEvent::CloseWnd {
wid: self.id(),
force,
});
}
fn into_shared(self) -> SharedWnd
where
Self: Sized + 'static,
{
Arc::new(Mutex::new(self))
}
async fn handle_update(&mut self) -> AResult<()>;
async fn handle_close(&mut self) -> bool;
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()>; async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()>;
async fn update(&mut self) -> AResult<()>;
async fn close(&mut self);
fn draw(&mut self, f: &mut Frame); fn draw(&mut self, f: &mut Frame);
} }
pub struct WndLoop {
upd_tx: Sender<()>,
updater: JoinHandle<()>,
window: SharedWnd,
}
impl WndLoop {
pub fn with_wnd(window: SharedWnd) -> Self {
let Channel { tx, rx } = Channel::new();
let wnd = window.clone();
let wnd_loop = async move {
loop {
rx.recv().unwrap();
wnd.lock().await.check_wnd_events().await;
}
};
WndLoop {
upd_tx: tx,
window,
updater: task::spawn(wnd_loop),
}
}
pub fn send_update(&self) {
self.upd_tx.send(()).unwrap();
}
}
pub struct WindowsHandler { pub struct WindowsHandler {
queue: Vec<Wnd>, queue: BTreeMap<WndId, WndLoop>,
term: Terminal<Backend>, term: Terminal<Backend>,
redraw_all: bool, redraw_all: bool,
} }
impl WindowsHandler { impl WindowsHandler {
fn get_last_wnd(&self) -> Wnd { fn get_last_wnd(&self) -> &WndLoop {
self.queue.last().expect("No windows found").clone() let last_id = self.queue.keys().rev().next().expect("No windows found");
self.queue.get(last_id).unwrap()
}
pub async fn lock<'l>() -> MutexGuard<'l, Self> {
WINDOWS.lock().await
} }
pub fn new(term: Terminal<Backend>) -> Self { fn new(term: Terminal<Backend>) -> Self {
Self { Self {
term, term,
queue: vec![], queue: BTreeMap::new(),
redraw_all: true, redraw_all: true,
} }
} }
pub fn push<W: Window + Default + 'static>(&mut self) { pub async fn push<W: Window + 'static>(&mut self, window: W) -> SharedWnd {
let window = Rc::new(RefCell::new(W::default())); self.push_dyn(window.into_shared()).await
self.queue.push(window); }
pub async fn push_new<W: Window + Default + 'static>(&mut self) -> SharedWnd {
self.push(W::default()).await
}
pub async fn push_dyn(&mut self, window: SharedWnd) -> SharedWnd {
let wid = window.lock().await.id();
self.queue.insert(wid, WndLoop::with_wnd(window.clone()));
window
} }
pub fn clear(&mut self) -> AResult<()> { pub fn clear(&mut self) -> AResult<()> {
@ -54,41 +165,57 @@ impl WindowsHandler {
Ok(()) Ok(())
} }
pub fn draw(&mut self) -> AResult<()> { pub async fn draw(&mut self) -> AResult<()> {
if self.redraw_all { let wids_to_redraw = if self.redraw_all {
for wnd in self.queue.iter() { self.redraw_all = false;
self.term.draw(|f| wnd.borrow_mut().draw(f))?; let mut wids = self.queue.keys().cloned().collect::<Vec<WndId>>();
} wids.sort();
wids
} else { } else {
let wnd = self.get_last_wnd(); vec![*WndId::last_wid()]
self.term.draw(|f| wnd.borrow_mut().draw(f))?; };
for wid in wids_to_redraw {
let mut wnd_locked = match self.queue.get(&wid) {
Some(w) => w.window.lock().await,
None => {
eprintln!("Can't redraw window {:?}, not found", wid);
continue;
}
};
self.term.draw(move |f| wnd_locked.draw(f))?;
} }
Ok(()) Ok(())
} }
pub async fn close(&mut self) { pub async fn close(&mut self, wid: WndId, force: bool) {
let wnd = self.queue.pop().unwrap(); let wnd = match self.queue.get(&wid) {
wnd.borrow_mut().close().await; Some(w) => w.clone(),
self.redraw_all = true; None => {
eprintln!("Can't close window {:?}, not found", wid);
return;
}
};
if wnd.window.lock().await.handle_close().await || force {
let WndLoop { updater, .. } = self.queue.remove(&wid).unwrap();
updater.abort();
self.redraw_all = true;
}
if self.queue.is_empty() { if self.queue.is_empty() {
self.show_cursor().unwrap(); self.show_cursor().unwrap();
teardown().unwrap();
exit(0); exit(0);
} }
} }
pub async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> { pub fn send_handle_kbd(&self, k: KeyCode) {
if let KeyCode::Esc = k { WND_EVENT_CHANNEL.send(WndEvent::Key(k));
self.close().await;
} else {
let current_wnd = self.get_last_wnd();
current_wnd.borrow_mut().handle_kbd(k).await?;
}
Ok(())
} }
pub async fn update(&mut self) -> AResult<()> { pub async fn update(&mut self) -> AResult<()> {
let current_wnd = self.get_last_wnd(); let current_wnd = self.get_last_wnd();
current_wnd.borrow_mut().update().await?; current_wnd.send_update();
current_wnd.window.lock().await.handle_update().await?;
Ok(()) Ok(())
} }
} }

@ -29,11 +29,11 @@ async fn test_setup_tasks() -> TestResult {
let job_alias = "passwd_contents"; let job_alias = "passwd_contents";
let cmd = format!("jobs add --alias {} 'cat /etc/passwd'", job_alias); let cmd = format!("jobs add --alias {} 'cat /etc/passwd'", job_alias);
Panel::check_status(cmd); Panel::check_status(cmd);
let cmd = format!("jobmap add {} {}", agent_uid, job_alias); let cmd = format!("map add {} {}", agent_uid, job_alias);
let assigned_uids: Vec<Uuid> = Panel::check_output(cmd); let assigned_uids: Vec<Uuid> = Panel::check_output(cmd);
for _ in 0..3 { for _ in 0..3 {
let result: Vec<AssignedJob> = let result: Vec<AssignedJob> =
Panel::check_output(format!("jobmap list {}", assigned_uids[0])); Panel::check_output(format!("map list {}", assigned_uids[0]));
if result[0].state == JobState::Finished { if result[0].state == JobState::Finished {
return Ok(()); return Ok(());
} else { } else {

@ -35,7 +35,8 @@ impl Panel {
}) })
} }
pub fn output<T: DeserializeOwned>(args: impl Into<String>) -> PanelResult<T> { pub fn output<T: DeserializeOwned>(args: impl Into<String> + Display) -> PanelResult<T> {
println!("Executing 'u_panel {}'", &args);
let splitted = split(args.into().as_ref()).unwrap(); let splitted = split(args.into().as_ref()).unwrap();
Self::output_argv( Self::output_argv(
splitted splitted
@ -54,12 +55,11 @@ impl Panel {
} }
pub fn check_status(args: impl Into<String> + Display) { pub fn check_status(args: impl Into<String> + Display) {
println!("Panel: executing '{}'", &args);
let result: PanelResult<Value> = Self::output(args); let result: PanelResult<Value> = Self::output(args);
Self::status_is_ok(result); Self::status_is_ok(result);
} }
pub fn check_output<T: DeserializeOwned>(args: impl Into<String>) -> T { pub fn check_output<T: DeserializeOwned>(args: impl Into<String> + Display) -> T {
let result = Self::output(args); let result = Self::output(args);
Self::status_is_ok(result) Self::status_is_ok(result)
} }

@ -12,7 +12,7 @@ impl JobBuilder {
let mut prepared: Vec<DynFut> = vec![]; let mut prepared: Vec<DynFut> = vec![];
let mut result = CombinedResult::<JobBuilder, UErrorBt>::new(); let mut result = CombinedResult::<JobBuilder, UErrorBt>::new();
for req in job_requests { for req in job_requests {
let job_meta = JobCache::get(&req.job_id); let job_meta = JobCache::get(req.job_id);
if job_meta.is_none() { if job_meta.is_none() {
result.err(UError::NoJob(req.job_id).into_bt()); result.err(UError::NoJob(req.job_id).into_bt());
continue; continue;
@ -22,7 +22,7 @@ impl JobBuilder {
let built_req = (|| -> UResult<()> { let built_req = (|| -> UResult<()> {
Ok(match job_meta.exec_type { Ok(match job_meta.exec_type {
JobType::Shell => { JobType::Shell => {
let meta = JobCache::get(&req.job_id).ok_or(UError::NoJob(req.job_id))?; let meta = JobCache::get(req.job_id).ok_or(UError::NoJob(req.job_id))?;
let curr_platform = guess_host_triple().unwrap_or("unknown").to_string(); let curr_platform = guess_host_triple().unwrap_or("unknown").to_string();
//TODO: extend platform checking (partial check) //TODO: extend platform checking (partial check)
if meta.platform != curr_platform { if meta.platform != curr_platform {

@ -20,11 +20,11 @@ impl JobCache {
JOB_CACHE.write().unwrap().insert(job_meta.id, job_meta); JOB_CACHE.write().unwrap().insert(job_meta.id, job_meta);
} }
pub fn contains(uid: &Uuid) -> bool { pub fn contains(uid: Uuid) -> bool {
JOB_CACHE.read().unwrap().contains_key(uid) JOB_CACHE.read().unwrap().contains_key(&uid)
} }
pub fn get(uid: &Uuid) -> Option<JobCacheHolder> { pub fn get<'jh>(uid: Uuid) -> Option<JobCacheHolder<'jh>> {
if !Self::contains(uid) { if !Self::contains(uid) {
return None; return None;
} }
@ -32,17 +32,17 @@ impl JobCache {
Some(JobCacheHolder(lock, uid)) Some(JobCacheHolder(lock, uid))
} }
pub fn remove(uid: &Uuid) { pub fn remove(uid: Uuid) {
JOB_CACHE.write().unwrap().remove(uid); JOB_CACHE.write().unwrap().remove(&uid);
} }
} }
pub struct JobCacheHolder<'jm>(pub RwLockReadGuard<'jm, Cache>, pub &'jm Uuid); pub struct JobCacheHolder<'jh>(pub RwLockReadGuard<'jh, Cache>, pub Uuid);
impl<'jm> Deref for JobCacheHolder<'jm> { impl<'jh> Deref for JobCacheHolder<'jh> {
type Target = JobMeta; type Target = JobMeta;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.0.get(self.1).unwrap() self.0.get(&self.1).unwrap()
} }
} }

@ -80,7 +80,7 @@ impl AssignedJob {
pub async fn run(mut self) -> Reportable { pub async fn run(mut self) -> Reportable {
use tokio::process::Command; use tokio::process::Command;
let (argv, _payload) = { let (argv, _payload) = {
let meta = JobCache::get(&self.job_id).unwrap(); let meta = JobCache::get(self.job_id).unwrap();
if let Some(ref payload) = meta.payload { if let Some(ref payload) = meta.payload {
let extracted_payload = match TempFile::write_exec(payload) { let extracted_payload = match TempFile::write_exec(payload) {
Ok(p) => p, Ok(p) => p,

Loading…
Cancel
Save