i realized that arch is shitty so switch to web frontend

pull/1/head
plazmoid 3 years ago
parent 5df2b09ceb
commit 16514b7d1a
  1. 29
      bin/u_panel/src/tui/mod.rs
  2. 25
      bin/u_panel/src/tui/retval.rs
  3. 20
      bin/u_panel/src/tui/utils.rs
  4. 61
      bin/u_panel/src/tui/windows/confirm.rs
  5. 30
      bin/u_panel/src/tui/windows/main_wnd.rs
  6. 87
      bin/u_panel/src/tui/windows/mod.rs
  7. 11
      lib/u_lib/src/builder.rs
  8. 2
      lib/u_lib/src/errors/variants.rs

@ -1,5 +1,4 @@
mod impls;
mod retval;
mod utils;
mod windows;
@ -12,7 +11,6 @@ use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use once_cell::sync::Lazy;
use retval::{RetVal, ReturnValue};
use std::{
env,
io::{stdout, Stdout},
@ -24,15 +22,14 @@ use std::{
};
use tui::{backend::CrosstermBackend, Terminal};
use utils::{AsyncChannel, Channel};
use windows::{MainWnd, SharedWnd, WindowsHandler, WndId};
use windows::{MainWnd, SharedWnd, WndId, WM};
pub type Backend = CrosstermBackend<Stdout>;
pub type Frame<'f> = tui::Frame<'f, Backend>;
const EVENT_GEN_PERIOD: Duration = Duration::from_millis(120);
const EVENT_GEN_PERIOD: Duration = Duration::from_millis(70);
static GENERAL_EVENT_CHANNEL: Lazy<Channel<GEvent>> = Lazy::new(|| Channel::new());
static ACTIVE_LOOP: AtomicBool = AtomicBool::new(true);
enum GEvent {
@ -49,10 +46,10 @@ fn get_terminal() -> AResult<Terminal<Backend>> {
}
pub async fn init_tui(args: &TUIArgs) -> AResult<()> {
let gui = !args.nogui;
init_logger();
info!("Initializing u_panel");
let gui = !args.nogui;
init_signal_handlers(gui);
init_panic_handler(gui);
term_setup(gui)?;
@ -67,15 +64,18 @@ pub async fn init_tui(args: &TUIArgs) -> AResult<()> {
async fn init_loop(args: &TUIArgs) -> AResult<()> {
let gui = !args.nogui;
if gui {
WindowsHandler::lock().await.clear()?;
WM::lock().await.clear()?;
}
WindowsHandler::lock().await.push_new::<MainWnd>().await;
WM::lock().await.push_new::<MainWnd>().await;
thread::spawn(move || {
while is_running() {
if event::poll(EVENT_GEN_PERIOD).unwrap() {
match event::read().unwrap() {
Event::Key(key) => GENERAL_EVENT_CHANNEL.send(GEvent::Key(key.code)),
Event::Key(key) => {
GENERAL_EVENT_CHANNEL.send(GEvent::Key(key.code));
GENERAL_EVENT_CHANNEL.send(GEvent::Tick);
}
_ => (),
}
} else {
@ -87,25 +87,25 @@ async fn init_loop(args: &TUIArgs) -> AResult<()> {
while is_running() {
match GENERAL_EVENT_CHANNEL.recv() {
GEvent::Tick => {
let mut wh = WindowsHandler::lock().await;
let mut wh = WM::lock().await;
wh.update().await?;
if gui {
wh.draw().await?;
}
}
GEvent::CloseWnd { wid, force } => {
WindowsHandler::lock().await.close(wid, force).await;
WM::lock().await.close(wid, force).await;
}
GEvent::Key(key) => {
info!(?key, "pressed");
if let KeyCode::Char('q') = key {
break_global();
} else {
WindowsHandler::lock().await.send_handle_kbd(key).await;
WM::lock().await.send_handle_kbd(key).await;
}
}
GEvent::CreateWnd(wnd) => {
WindowsHandler::lock().await.push_dyn(wnd).await;
WM::lock().await.push_dyn(wnd).await;
}
GEvent::Exit => {
break_global();
@ -163,7 +163,8 @@ fn init_logger() {
fn init_panic_handler(gui: bool) {
set_hook(Box::new(move |panic_info| {
term_teardown(gui).ok();
eprintln!("{}\n{:?}", panic_info, Backtrace::new());
eprintln!("CRITICAL PANIK ENCOUNTRD OMFG!11! go see logz lul");
error!("{}\n{:?}", panic_info, Backtrace::new());
exit(254);
}));
}

@ -1,25 +0,0 @@
use crate::tui::Channel;
use once_cell::sync::Lazy;
use std::any::{type_name, Any};
type RV = Box<dyn Any + Send>;
static RETVAL: Lazy<Channel<RV>> = Lazy::new(|| Channel::new());
pub struct ReturnValue;
impl ReturnValue {
pub fn set(t: RV) {
RETVAL.send(t);
}
pub fn get<T: 'static>() -> Box<T> {
RETVAL
.recv()
.downcast::<T>()
.expect(&format!("wrong type {}", type_name::<T>()))
}
}
pub trait RetVal {
fn retval(&self) -> RV;
}

@ -1,5 +1,6 @@
use async_channel::{
unbounded as unbounded_async, Receiver as AsyncReceiver, Sender as AsyncSender,
unbounded as unbounded_async, Receiver as AsyncReceiver, RecvError, SendError,
Sender as AsyncSender,
};
use crossbeam::channel::{unbounded, Receiver, Sender};
@ -21,10 +22,6 @@ impl<T> Channel<T> {
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> {
@ -33,6 +30,7 @@ impl<T> Default for Channel<T> {
}
}
#[derive(Clone)]
pub struct AsyncChannel<T> {
pub tx: AsyncSender<T>,
pub rx: AsyncReceiver<T>,
@ -44,16 +42,12 @@ impl<T> AsyncChannel<T> {
Self { tx, rx }
}
pub async fn send(&self, msg: T) {
self.tx.send(msg).await.unwrap()
}
pub async fn recv(&self) -> T {
self.rx.recv().await.unwrap()
pub async fn send(&self, msg: T) -> Result<(), SendError<T>> {
self.tx.send(msg).await
}
pub fn is_empty(&self) -> bool {
self.rx.is_empty()
pub async fn recv(&self) -> Result<T, RecvError> {
self.rx.recv().await
}
}

@ -1,14 +1,10 @@
use super::{Window, WndId};
use crate::tui::{
windows::WndEvent, AsyncChannel, Frame, GEvent, RetVal, ReturnValue, GENERAL_EVENT_CHANNEL,
};
use super::{ReturnVal, Window, WndId};
use crate::tui::Frame;
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use once_cell::sync::OnceCell;
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};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph};
#[derive(Default)]
pub struct ConfirmWnd {
@ -19,15 +15,15 @@ pub struct ConfirmWnd {
}
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,
pub fn new(msg: impl Into<String>, variants: &[&str]) -> Self {
let variants = if !variants.is_empty() {
variants
} else {
&["Yes", "No"]
};
let mut this = Self {
msg: msg.into(),
variants,
variants: variants.into_iter().map(|s| s.to_string()).collect(),
..Default::default()
};
this.state.select(Some(0));
@ -48,24 +44,14 @@ impl ConfirmWnd {
}
}
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,
KeyCode::Enter => self.close(false, true).await,
KeyCode::Esc => self.close(false, false).await,
_ => (),
}
Ok(())
@ -82,10 +68,13 @@ impl Window for ConfirmWnd {
fn draw(&mut self, f: &mut Frame) {
let size = f.size();
let rect = centered_rect(60, 40, size);
let popup = Block::default().title("Popup").borders(Borders::ALL);
f.render_widget(Clear, rect);
f.render_widget(popup, rect);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)])
.constraints(vec![Constraint::Percentage(40), Constraint::Percentage(30)])
.split(rect);
let msg = Paragraph::new(self.msg.as_ref());
f.render_widget(msg, chunks[0]);
@ -97,22 +86,20 @@ impl Window for ConfirmWnd {
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list =
List::new(options).highlight_style(Style::default().add_modifier(Modifier::BOLD));
List::new(options).highlight_style(Style::default().bg(Color::Gray).fg(Color::Black));
f.render_stateful_widget(list, chunks[1], &mut self.state);
}
fn get_chan(&self) -> &'static AsyncChannel<WndEvent> {
static EV_CHAN: OnceCell<AsyncChannel<WndEvent>> = OnceCell::new();
EV_CHAN.get_or_init(|| AsyncChannel::new())
fn retval(&self) -> ReturnVal {
let value = self
.variants
.get(self.state.selected().unwrap())
.unwrap()
.to_owned();
ReturnVal::String(value)
}
}
pub async fn confirm_wnd(msg: impl Into<String>) -> String {
let wnd = ConfirmWnd::new_yn(msg.into(), None);
GENERAL_EVENT_CHANNEL.send(GEvent::CreateWnd(wnd.into_shared()));
*ReturnValue::get()
}
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let popup_layout = Layout::default()
.direction(Direction::Vertical)

@ -1,13 +1,7 @@
use super::{confirm_wnd, Window};
use crate::tui::{
impls::CRUD,
windows::{WndEvent, WndId},
AsyncChannel, Frame, RetVal,
};
use super::{ConfirmWnd, ReturnVal, Window};
use crate::tui::{impls::CRUD, windows::WndId, Frame};
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use once_cell::sync::OnceCell;
use std::any::Any;
use std::{fmt::Display, str::FromStr};
use strum::VariantNames;
use tokio::join;
@ -205,6 +199,7 @@ impl MainWnd {
UiTabs::Agents => {
self.agents.updated = false;
self.jobs.updated = false;
self.map.updated = false;
}
UiTabs::Jobs => self.jobs.updated = false,
UiTabs::Map => self.map.updated = false,
@ -255,17 +250,21 @@ impl MainWnd {
impl Window for MainWnd {
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
match k {
KeyCode::Esc => self.close(false).await,
KeyCode::Esc => self.close(false, false).await,
KeyCode::Left => self.prev_tab(),
KeyCode::Right => self.next_tab(),
KeyCode::Up => self.on_up(),
KeyCode::Down => self.on_down(),
KeyCode::Delete => {
if &confirm_wnd("Delete?").await == "Yes" {
if let ReturnVal::String(ref s) =
ConfirmWnd::new("Delete?", &["Yes", "No"]).wait_retval()
{
if s == "Yes" {
self.delete().await;
self.update_tab();
}
}
}
KeyCode::F(5) => self.update_tab(),
_ => (),
};
@ -316,15 +315,8 @@ impl Window for MainWnd {
f.render_stateful_widget(list, chunks[1], self.tab_list_state());
}
fn get_chan(&self) -> &'static AsyncChannel<WndEvent> {
static EV_CHAN: OnceCell<AsyncChannel<WndEvent>> = OnceCell::new();
EV_CHAN.get_or_init(|| AsyncChannel::new())
}
}
impl RetVal for MainWnd {
fn retval(&self) -> Box<dyn Any + Send> {
Box::new(())
fn retval(&self) -> ReturnVal {
ReturnVal::None
}
}

@ -1,30 +1,34 @@
mod confirm;
mod main_wnd;
use async_channel::Sender;
pub use confirm::{confirm_wnd, ConfirmWnd};
pub use confirm::ConfirmWnd;
pub use main_wnd::MainWnd;
use crate::tui::{
get_terminal, impls::Id, AsyncChannel, Backend, Frame, GEvent, RetVal, ReturnValue,
GENERAL_EVENT_CHANNEL,
get_terminal, impls::Id, AsyncChannel, Backend, Channel, Frame, GEvent, GENERAL_EVENT_CHANNEL,
};
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use once_cell::sync::{Lazy, OnceCell};
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::{Mutex as StdMutex, MutexGuard as StdMutexGuard};
use std::sync::{Arc, Mutex as StdMutex, MutexGuard as StdMutexGuard};
use tokio::sync::{Mutex, MutexGuard};
use tokio::task::{self, JoinHandle};
use tui::Terminal;
static WINDOWS: Lazy<Arc<Mutex<WindowsHandler>>> =
Lazy::new(|| Arc::new(Mutex::new(WindowsHandler::new(get_terminal().unwrap()))));
static WINDOWS: Lazy<Arc<Mutex<WM>>> =
Lazy::new(|| Arc::new(Mutex::new(WM::new(get_terminal().unwrap()))));
static LAST_WND_ID: OnceCell<StdMutex<WndId>> = OnceCell::new();
static RETVAL: Lazy<Channel<ReturnVal>> = Lazy::new(|| Channel::new());
pub type SharedWnd = Arc<Mutex<dyn Window>>;
pub enum ReturnVal {
String(String),
None,
}
#[derive(Clone, Debug)]
pub enum WndEvent {
Key(KeyCode),
@ -51,7 +55,7 @@ impl Default for WndId {
}
#[async_trait]
pub trait Window: Id<WndId> + RetVal + Send {
pub trait Window: Id<WndId> + Send {
async fn handle_event(&mut self, ev: WndEvent) {
match ev {
WndEvent::Key(k) => {
@ -60,22 +64,27 @@ pub trait Window: Id<WndId> + RetVal + Send {
}
}
async fn close(&mut self, force: bool) {
let rv = self.retval();
ReturnValue::set(rv);
async fn close(&mut self, force: bool, save_retval: bool) {
let rv = if save_retval {
self.retval()
} else {
ReturnVal::None
};
RETVAL.send(rv);
GENERAL_EVENT_CHANNEL.send(GEvent::CloseWnd {
wid: self.id(),
force,
});
}
fn get_chan(&self) -> &'static AsyncChannel<WndEvent>;
fn retval(&self) -> ReturnVal;
fn into_shared(self) -> SharedWnd
fn wait_retval(self) -> ReturnVal
where
Self: Sized + 'static,
{
Arc::new(Mutex::new(self))
GENERAL_EVENT_CHANNEL.send(GEvent::CreateWnd(Arc::new(Mutex::new(self))));
RETVAL.recv()
}
async fn handle_update(&mut self) -> AResult<()>;
@ -88,27 +97,28 @@ pub trait Window: Id<WndId> + RetVal + Send {
}
pub struct WndLoop {
upd_tx: Sender<WndEvent>,
updater: JoinHandle<()>,
chan: AsyncChannel<WndEvent>,
wnd_loop: JoinHandle<()>,
window: SharedWnd,
}
impl WndLoop {
pub async fn with_wnd(window: SharedWnd) -> Self {
let wnd = window.clone();
let AsyncChannel { tx, rx } = wnd.lock().await.get_chan();
let chan = AsyncChannel::<WndEvent>::new();
let ch = chan.clone();
let wnd_loop = async move {
loop {
match rx.recv().await {
match ch.recv().await {
Ok(ev) => wnd.lock().await.handle_event(ev).await,
Err(_) => break,
}
}
};
WndLoop {
upd_tx: tx.clone(),
chan,
window,
updater: task::spawn(wnd_loop),
wnd_loop: task::spawn(wnd_loop),
}
}
@ -116,17 +126,16 @@ impl WndLoop {
let wnd_id = self.window.lock().await.id();
let event = ev.clone();
debug!(?event, ?wnd_id, "sending");
self.upd_tx.send(ev).await.expect("big zhopa");
self.chan.send(ev).await.expect("send failed");
}
}
pub struct WindowsHandler {
pub struct WM {
queue: BTreeMap<WndId, WndLoop>,
term: Terminal<Backend>,
redraw_all: bool,
}
impl WindowsHandler {
impl WM {
fn get_last_wnd(&self) -> &WndLoop {
let last_id = self.queue.keys().rev().next().expect("No windows found");
self.queue.get(last_id).unwrap()
@ -140,12 +149,11 @@ impl WindowsHandler {
Self {
term,
queue: BTreeMap::new(),
redraw_all: true,
}
}
pub async fn push<W: Window + 'static>(&mut self, window: W) -> SharedWnd {
self.push_dyn(window.into_shared()).await
self.push_dyn(Arc::new(Mutex::new(window))).await
}
pub async fn push_new<W: Window + Default + 'static>(&mut self) -> SharedWnd {
@ -165,17 +173,17 @@ impl WindowsHandler {
}
pub async fn draw(&mut self) -> AResult<()> {
let wids_to_redraw = if self.redraw_all {
self.redraw_all = false;
self.queue.keys().collect::<Vec<&WndId>>()
} else {
vec![self.queue.keys().last().unwrap()]
};
for wid in wids_to_redraw {
for wid in self.queue.keys().collect::<Vec<&WndId>>() {
let mut wnd_locked = match self.queue.get(&wid) {
Some(w) => w.window.lock().await,
Some(w) => match w.window.try_lock() {
Ok(w) => w,
Err(_) => {
warn!("Can't lock window {:?}", wid);
continue;
}
},
None => {
eprintln!("Can't redraw window {:?}, not found", wid);
warn!("Can't redraw window {:?}, not found", wid);
continue;
}
};
@ -188,14 +196,13 @@ impl WindowsHandler {
let wnd = match self.queue.get(&wid) {
Some(w) => w.clone(),
None => {
eprintln!("Can't close window {:?}, not found", wid);
warn!("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;
let WndLoop { wnd_loop, .. } = self.queue.remove(&wid).unwrap();
wnd_loop.abort();
}
if self.queue.is_empty() {

@ -1,4 +1,13 @@
use crate::{UError, UErrorBt, UResult, cache::JobCache, executor::{Waiter, DynFut}, messaging::Reportable, models::{Agent, AssignedJob, JobMeta, JobType}, utils::{CombinedResult, OneOrVec}};
use crate::{
UError,
UErrorBt,
UResult,
cache::JobCache,
executor::{Waiter, DynFut},
messaging::Reportable,
models::{Agent, AssignedJob, JobMeta, JobType},
utils::{CombinedResult, OneOrVec}
};
use guess_host_triple::guess_host_triple;
use std::collections::HashMap;

@ -66,7 +66,7 @@ pub enum UError {
#[error("Panicked: {0}")]
Panic(String),
#[error("UI init error: {0}")]
#[error("UI error: {0}")]
TUIError(String),
}

Loading…
Cancel
Save