dnetview/ View: refactor

* create view objects to handle model data
* update() function to write model to view
* render() to draw view

this commit deprecates ui::ui()
This commit is contained in:
lunar-mining
2022-04-26 14:00:56 +02:00
parent 30d8b177dd
commit 8b5f56f54e
5 changed files with 434 additions and 135 deletions

View File

@@ -8,5 +8,5 @@ pub mod view;
pub use config::{DnvConfig, CONFIG_FILE_CONTENTS};
//pub use model::Model;
pub use options::ProgramOptions;
pub use ui::ui;
//pub use ui::ui;
//pub use view::{IdListView, InfoListView, View};

View File

@@ -31,7 +31,7 @@ use dnetview::{
options::ProgramOptions,
ui,
util::{generate_id, make_connect_id, make_node_id, make_session_id},
view::{IdListView, InfoListView, View},
view::{ConnectInfoView, IdListView, InfoListView, NodeInfoView, SessionInfoView, View},
};
struct DNetView {
@@ -108,7 +108,7 @@ async fn main() -> Result<()> {
terminal.clear()?;
let ids = Mutex::new(FxHashSet::default());
let infos = Mutex::new(FxHashMap::default());
let infos = Mutex::new(Vec::new());
let model = Arc::new(Model::new(ids, infos));
@@ -180,10 +180,11 @@ async fn parse_data(
let node_info = NodeInfo::new(node_id.clone(), node_name.to_string(), sessions.clone());
let node = SelectableObject::Node(node_info.clone());
// we might simplify things by making this a Vec<SelectableObject>
update_model(model.clone(), sessions.clone(), node_info, node).await?;
debug!("IDS: {:?}", model.ids.lock().await);
debug!("INFOS: {:?}", model.infos.lock().await);
//debug!("IDS: {:?}", model.ids.lock().await);
//debug!("INFOS: {:?}", model.infos.lock().await);
Ok(())
}
@@ -194,19 +195,27 @@ async fn update_model(
node_info: NodeInfo,
node: SelectableObject,
) -> Result<()> {
model.ids.lock().await.insert(node_info.node_id.clone());
model.infos.lock().await.push(node);
for session in sessions.clone() {
model.ids.lock().await.insert(session.session_id);
model.ids.lock().await.insert(session.clone().session_id);
let session_obj = SelectableObject::Session(session.clone());
model.infos.lock().await.push(session_obj);
for connect in session.children {
model.ids.lock().await.insert(connect.connect_id);
model.ids.lock().await.insert(connect.clone().connect_id);
let connect_obj = SelectableObject::Connect(connect.clone());
model.infos.lock().await.push(connect_obj);
}
}
model.ids.lock().await.insert(node_info.node_id.clone());
model.infos.lock().await.insert(node_info.node_id.clone(), node);
Ok(())
}
async fn parse_inbound(inbound: &Value, node_id: String) -> Result<SessionInfo> {
let session_name = "Inbound".to_string();
let session_type = Session::Inbound;
let session_id = make_session_id(node_id.clone(), &session_type)?;
let mut connects: Vec<ConnectInfo> = Vec::new();
@@ -254,8 +263,12 @@ async fn parse_inbound(inbound: &Value, node_id: String) -> Result<SessionInfo>
}
}
}
let session_info =
SessionInfo::new(session_id.clone(), node_id.clone(), connects.clone());
let session_info = SessionInfo::new(
session_name,
session_id.clone(),
node_id.clone(),
connects.clone(),
);
Ok(session_info)
}
None => Err(Error::ValueIsNotObject),
@@ -264,6 +277,7 @@ async fn parse_inbound(inbound: &Value, node_id: String) -> Result<SessionInfo>
// TODO: placeholder for now
async fn parse_manual(_manual: &Value, node_id: String) -> Result<SessionInfo> {
let session_name = "Manual".to_string();
let session_type = Session::Manual;
let mut connects: Vec<ConnectInfo> = Vec::new();
@@ -280,12 +294,13 @@ async fn parse_manual(_manual: &Value, node_id: String) -> Result<SessionInfo> {
let connect_info =
ConnectInfo::new(connect_id, addr, is_empty, msg, status, state, msg_log, parent);
connects.push(connect_info.clone());
let session_info = SessionInfo::new(session_id, node_id, connects.clone());
let session_info = SessionInfo::new(session_name, session_id, node_id, connects.clone());
Ok(session_info)
}
async fn parse_outbound(outbound: &Value, node_id: String) -> Result<SessionInfo> {
let session_name = "Outbound".to_string();
let session_type = Session::Outbound;
let mut connects: Vec<ConnectInfo> = Vec::new();
let slots = &outbound["slots"];
@@ -347,7 +362,8 @@ async fn parse_outbound(outbound: &Value, node_id: String) -> Result<SessionInfo
}
}
}
let session_info = SessionInfo::new(session_id, node_id, connects.clone());
let session_info =
SessionInfo::new(session_name, session_id, node_id, connects.clone());
Ok(session_info)
}
None => Err(Error::ValueIsNotObject),
@@ -361,15 +377,29 @@ async fn render<B: Backend>(terminal: &mut Terminal<B>, model: Arc<Model>) -> Re
let id_list = IdListView::new(FxHashSet::default());
let info_list = InfoListView::new(FxHashMap::default());
let mut view = View::new(id_list.clone(), info_list.clone());
let node_view = NodeInfoView::default();
let session_view = SessionInfoView::default();
let connect_view = ConnectInfoView::default();
let mut view = View::new(
id_list.clone(),
info_list,
node_view.clone(),
session_view.clone(),
connect_view.clone(),
);
view.id_list.state.select(Some(0));
view.info_list.index = 0;
loop {
view.update(model.infos.lock().await.clone());
//debug!("MODEL: {:?}", model.infos.lock().await.clone());
//debug!("VIEW BEFORE UPDATE: {:?}", &view.id_list.ids);
let mut view = view.clone().update(model.infos.lock().await.clone())?;
//debug!("VIEW AFTER UPDATE: {:?}", view.clone().id_list.ids);
terminal.draw(|f| {
ui::ui(f, view.clone());
view.clone().render(f);
})?;
for k in asi.by_ref().keys() {
match k.unwrap() {
@@ -378,10 +408,10 @@ async fn render<B: Backend>(terminal: &mut Terminal<B>, model: Arc<Model>) -> Re
return Ok(())
}
Key::Char('j') => {
view.id_list.next();
view.clone().id_list.next();
}
Key::Char('k') => {
view.id_list.previous();
view.clone().id_list.previous();
}
_ => (),
}

View File

@@ -19,14 +19,12 @@ pub enum SelectableObject {
pub struct Model {
pub ids: Mutex<FxHashSet<String>>,
pub infos: Mutex<FxHashMap<String, SelectableObject>>,
pub infos: Mutex<Vec<SelectableObject>>,
//pub infos: Mutex<FxHashMap<String, SelectableObject>>,
}
impl Model {
pub fn new(
ids: Mutex<FxHashSet<String>>,
infos: Mutex<FxHashMap<String, SelectableObject>>,
) -> Model {
pub fn new(ids: Mutex<FxHashSet<String>>, infos: Mutex<Vec<SelectableObject>>) -> Model {
Model { ids, infos }
}
}
@@ -46,14 +44,20 @@ impl NodeInfo {
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
pub struct SessionInfo {
pub session_name: String,
pub session_id: String,
pub parent: String,
pub children: Vec<ConnectInfo>,
}
impl SessionInfo {
pub fn new(session_id: String, parent: String, children: Vec<ConnectInfo>) -> SessionInfo {
SessionInfo { session_id, parent, children }
pub fn new(
session_name: String,
session_id: String,
parent: String,
children: Vec<ConnectInfo>,
) -> SessionInfo {
SessionInfo { session_name, session_id, parent, children }
}
}

View File

@@ -1,99 +1,141 @@
use crate::view::View;
use log::debug;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
style::Style,
text::{Span, Spans},
widgets::{Block, Borders, List, ListItem, Paragraph},
Frame,
};
pub fn ui<B: Backend>(f: &mut Frame<'_, B>, mut view: View) {
let list_margin = 2;
let list_direction = Direction::Horizontal;
let list_cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)];
let mut nodes = Vec::new();
let style = Style::default();
for id in &view.id_list.ids {
let id_span = Span::raw(id.to_string());
let mut lines = vec![Spans::from(id_span)];
match &view.info_list.infos.get(id) {
Some(node) => {
//debug!("NODE: {:?}", node);
//if !node.outbound.iter().all(|node| node.is_empty) {
// lines.push(Spans::from(Span::styled(" Outgoing", Style::default())));
//}
//for outbound in &node.outbound.clone() {
// for slot in outbound.slots.clone() {
// let addr = Span::styled(format!(" {}", slot.addr), style);
// let msg: Span = match slot.channel.last_status.as_str() {
// "recv" => Span::styled(
// format!(" [R: {}]", slot.channel.last_msg),
// style,
// ),
// "sent" => Span::styled(
// format!(" [S: {}]", slot.channel.last_msg),
// style,
// ),
// a => Span::styled(a.to_string(), style),
// };
// lines.push(Spans::from(vec![addr, msg]));
// }
//}
//if !node.inbound.iter().all(|node| node.is_empty) {
// lines.push(Spans::from(Span::styled(" Incoming", Style::default())));
//}
//for inbound in &node.inbound {
// let addr = Span::styled(format!(" {}", inbound.connected), style);
// let msg: Span = match inbound.channel.last_status.as_str() {
// "recv" => Span::styled(
// format!(" [R: {}]", inbound.channel.last_msg),
// style,
// ),
// "sent" => Span::styled(
// format!(" [R: {}]", inbound.channel.last_msg),
// style,
// ),
// a => Span::styled(a.to_string(), style),
// };
// lines.push(Spans::from(vec![addr, msg]));
//}
//lines.push(Spans::from(Span::styled(" Manual", Style::default())));
//for connect in &node.manual {
// lines.push(Spans::from(Span::styled(format!(" {}", connect.key), style)));
//}
}
None => {
// TODO
debug!("This is also a bug");
}
}
let ids = ListItem::new(lines);
nodes.push(ids);
}
let nodes =
List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
let slice = Layout::default()
.direction(list_direction)
.margin(list_margin)
.constraints(list_cnstrnts)
.split(f.size());
f.render_stateful_widget(nodes, slice[0], &mut view.id_list.state);
render_info_right(view.clone(), f, slice);
}
fn render_info_right<B: Backend>(_view: View, f: &mut Frame<'_, B>, slice: Vec<Rect>) {
let span = vec![];
let graph =
Paragraph::new(span).block(Block::default().borders(Borders::ALL)).style(Style::default());
f.render_widget(graph, slice[1]);
}
//use crate::model::SelectableObject;
//use crate::view::View;
//use log::debug;
//
//use tui::{
// backend::Backend,
// layout::{Constraint, Direction, Layout, Rect},
// style::Style,
// text::{Span, Spans},
// widgets::{Block, Borders, List, ListItem, Paragraph},
// Frame,
//};
//
//pub fn ui<B: Backend>(f: &mut Frame<'_, B>, mut view: View) {
// let list_margin = 2;
// let list_direction = Direction::Horizontal;
// let list_cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)];
//
// let mut nodes = Vec::new();
// let style = Style::default();
//
// // TODO: this is insanely nested. pass SelectableObjects to different views
// for id in &view.id_list.ids {
// let mut lines: Vec<Spans> = Vec::new();
// //debug!("{}", id);
// // this only needs to be node ids
// match &view.info_list.infos.get(id) {
// Some(node) => {
// match node {
// SelectableObject::Node(node_info) => {
// let name_span = Span::raw(node_info.node_name.to_string());
// lines.push(Spans::from(name_span));
// //let mut lines = vec![Spans::from(name_span)];
// //let id = &node_info.node_id;
// //let name = &node_info.node_name;
// for child in &node_info.children {
// match child.session_name.as_str() {
// "Outgoing" => {
// lines.push(Spans::from(Span::styled(
// " Outgoing",
// Style::default(),
// )));
// }
// "Incoming" => {
// lines.push(Spans::from(Span::styled(
// " Outgoing",
// Style::default(),
// )));
// }
// "Manual" => {
// lines.push(Spans::from(Span::styled(
// " Outgoing",
// Style::default(),
// )));
// }
// _ => {}
// }
// for child in &child.children {
// // do something
// }
// }
// //if !node.outbound.iter().all(|node| node.is_empty) {
// // lines.push(Spans::from(Span::styled(" Outgoing", Style::default())));
// //}
//
// // ok
// }
// _ => {
// // ok
// }
// }
// //for outbound in &node.outbound.clone() { LOG_TARGETS=net cargo run -- -vv --slots 5 --seed 0.0.0.0:9999 --irc 127.0.0.1:6668 --rpc 127.0.0.1:8000
// // for slot in outbound.slots.clone() {
// // let addr = Span::styled(format!(" {}", slot.addr), style);
// // let msg: Span = match slot.channel.last_status.as_str() {
// // "recv" => Span::styled(
// // format!(" [R: {}]", slot.channel.last_msg),
// // style,
// // ),
// // "sent" => Span::styled(
// // format!(" [S: {}]", slot.channel.last_msg),
// // style,
// // ),
// // a => Span::styled(a.to_string(), style),
// // };
// // lines.push(Spans::from(vec![addr, msg]));
// // }
// //}
// //if !node.inbound.iter().all(|node| node.is_empty) {
// // lines.push(Spans::from(Span::styled(" Incoming", Style::default())));
// //}
// //for inbound in &node.inbound {
// // let addr = Span::styled(format!(" {}", inbound.connected), style);
// // let msg: Span = match inbound.channel.last_status.as_str() {
// // "recv" => Span::styled(
// // format!(" [R: {}]", inbound.channel.last_msg),
// // style,
// // ),
// // "sent" => Span::styled(
// // format!(" [R: {}]", inbound.channel.last_msg),
// // style,
// // ),
// // a => Span::styled(a.to_string(), style),
// // };
// // lines.push(Spans::from(vec![addr, msg]));
// //}
// //lines.push(Spans::from(Span::styled(" Manual", Style::default())));
// //for connect in &node.manual {
// // lines.push(Spans::from(Span::styled(format!(" {}", connect.key), style)));
// //}
// }
// None => {
// // TODO
// debug!("This is also a bug");
// }
// }
//
// // need list of all ids here
// let ids = ListItem::new(lines);
// nodes.push(ids);
// }
//
// let nodes =
// List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
// let slice = Layout::default()
// .direction(list_direction)
// .margin(list_margin)
// .constraints(list_cnstrnts)
// .split(f.size());
//
// f.render_stateful_widget(nodes, slice[0], &mut view.id_list.state);
//
// render_info_right(view.clone(), f, slice);
//}
//
//fn render_info_right<B: Backend>(_view: View, f: &mut Frame<'_, B>, slice: Vec<Rect>) {
// let span = vec![];
// let graph =
// Paragraph::new(span).block(Block::default().borders(Borders::ALL)).style(Style::default());
// f.render_widget(graph, slice[1]);
//}

View File

@@ -1,29 +1,252 @@
use async_std::sync::Arc;
use darkfi::error::Result;
use fxhash::{FxHashMap, FxHashSet};
//use log::debug;
use log::debug;
use serde::{Deserialize, Serialize};
use tui::widgets::ListState;
use crate::model::SelectableObject;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
style::Style,
text::{Span, Spans},
widgets::{Block, Borders, List, ListItem, Paragraph},
Frame,
};
#[derive(Clone)]
use crate::model::{ConnectInfo, Model, NodeInfo, SelectableObject, SessionInfo};
#[derive(Debug, Clone)]
pub struct View {
pub id_list: IdListView,
pub info_list: InfoListView,
pub node_info: NodeInfoView,
pub session_info: SessionInfoView,
pub connect_info: ConnectInfoView,
}
impl View {
pub fn new(id_list: IdListView, info_list: InfoListView) -> View {
View { id_list, info_list }
pub fn new(
id_list: IdListView,
info_list: InfoListView,
node_info: NodeInfoView,
session_info: SessionInfoView,
connect_info: ConnectInfoView,
) -> View {
View { id_list, info_list, node_info, session_info, connect_info }
}
pub fn update(&mut self, infos: FxHashMap<String, SelectableObject>) {
for (id, info) in infos {
self.id_list.ids.insert(id.clone());
self.info_list.infos.insert(id, info);
pub fn update(mut self, model: Vec<SelectableObject>) -> Result<View> {
for obj in model {
let obj_clone = obj.clone();
match obj {
SelectableObject::Node(node) => {
let node1 = node.clone();
//self.node_info.clone().update(node1.clone())?;
self.id_list.ids.insert(node1.clone().node_id);
self.info_list.infos.insert(node.node_id, obj_clone);
}
SelectableObject::Session(session) => {
let session1 = session.clone();
//self.session_info.clone().update(session1.clone())?;
self.id_list.ids.insert(session1.clone().session_id);
self.info_list.infos.insert(session1.clone().session_id, obj_clone);
}
SelectableObject::Connect(connect) => {
let connect1 = connect.clone();
//self.connect_info.clone().update(connect)?;
self.id_list.ids.insert(connect1.clone().connect_id);
self.info_list.infos.insert(connect1.clone().connect_id, obj_clone);
}
}
}
let id_list = self.id_list;
let info_list = self.info_list;
let node_info = self.node_info;
let session_info = self.session_info;
let connect_info = self.connect_info;
Ok(View { id_list, info_list, node_info, session_info, connect_info })
}
pub fn render<B: Backend>(mut self, f: &mut Frame<'_, B>) {
//debug!("VIEW AT RENDER {:?}", self.id_list.ids);
//let mut nodes = Vec::new();
let list_margin = 2;
let list_direction = Direction::Horizontal;
let list_cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)];
let mut nodes = Vec::new();
for id in self.id_list.ids {
match self.info_list.infos.get(&id) {
Some(obj) => {
match obj {
SelectableObject::Node(info) => {
//debug!("FOUND NODE: {:?}", info);
let name_span = Span::raw(&info.node_name);
let lines = vec![Spans::from(name_span)];
//lines.push(Spans::from(name_span));
let names = ListItem::new(lines);
nodes.push(names);
//nodes.push(node);
}
SelectableObject::Session(info) => self.session_info.clone().render(info),
SelectableObject::Connect(info) => self.connect_info.clone().render(info),
}
//
}
None => {
// TODO
} //
}
//
}
let slice = Layout::default()
.direction(list_direction)
.margin(list_margin)
.constraints(list_cnstrnts)
.split(f.size());
let nodes =
List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
f.render_stateful_widget(nodes, slice[0], &mut self.id_list.state);
//for node in nodes {
// f.render_stateful_widget(node, slice[0], &mut self.id_list.state);
//}
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct NodeInfoView {
pub node_id: String,
pub node_name: String,
pub children: Vec<SessionInfo>,
}
impl NodeInfoView {
pub fn default() -> NodeInfoView {
let node_id = String::new();
let node_name = String::new();
let children: Vec<SessionInfo> = Vec::new();
NodeInfoView { node_id, node_name, children }
}
pub fn update(mut self, data: NodeInfo) -> Result<()> {
//self.state.slect(Some(0));
self.node_id = data.node_id;
self.node_name = data.node_name;
self.children = data.children;
Ok(())
}
pub fn render(self, node: &NodeInfo) -> List {
let mut nodes = Vec::new();
let mut lines: Vec<Spans> = Vec::new();
let name_span = Span::raw(&node.node_name);
lines.push(Spans::from(name_span));
let ids = ListItem::new(lines);
nodes.push(ids);
let nodes =
List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
nodes
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
pub struct SessionInfoView {
pub session_name: String,
pub session_id: String,
pub parent: String,
pub children: Vec<ConnectInfo>,
}
impl SessionInfoView {
pub fn default() -> SessionInfoView {
let session_name = String::new();
let session_id = String::new();
let parent = String::new();
let children: Vec<ConnectInfo> = Vec::new();
SessionInfoView { session_name, session_id, parent, children }
}
pub fn update(mut self, data: SessionInfo) -> Result<()> {
self.session_name = data.session_name;
self.session_id = data.session_id;
self.parent = data.parent;
self.children = data.children;
Ok(())
}
pub fn render(self, session: &SessionInfo) {
//let mut lines: Vec<Spans> = Vec::new();
//let name_span = Span::raw(self.session_name);
//let ids = ListItem::new(lines);
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
pub struct ConnectInfoView {
pub connect_id: String,
pub addr: String,
pub is_empty: bool,
pub last_msg: String,
pub last_status: String,
pub state: String,
pub msg_log: Vec<String>,
pub parent: String,
}
impl ConnectInfoView {
pub fn default() -> ConnectInfoView {
let connect_id = String::new();
let addr = String::new();
let is_empty = true;
let last_msg = String::new();
let last_status = String::new();
let state = String::new();
let msg_log: Vec<String> = Vec::new();
let parent = String::new();
ConnectInfoView {
connect_id,
addr,
is_empty,
last_msg,
last_status,
state,
msg_log,
parent,
}
}
pub fn update(mut self, data: ConnectInfo) -> Result<()> {
self.connect_id = data.connect_id;
self.addr = data.addr;
self.is_empty = data.is_empty;
self.last_msg = data.last_msg;
self.last_status = data.last_status;
self.state = data.state;
self.msg_log = data.msg_log;
self.parent = data.parent;
Ok(())
}
pub fn render(self, connect: &ConnectInfo) {
let mut lines: Vec<Spans> = Vec::new();
//let name_span = Span::raw(self.connect_id);
}
}
//#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
//pub enum SelectableObject {
// Node(NodeInfoView),
// Session(SessionInfoView),
// Connect(ConnectInfoViewView),
//}
#[derive(Debug, Clone)]
pub struct IdListView {
pub state: ListState,
pub ids: FxHashSet<String>,
@@ -66,7 +289,7 @@ impl IdListView {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct InfoListView {
pub index: usize,
pub infos: FxHashMap<String, SelectableObject>,