view.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2022 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use fxhash::FxHashMap;
  19. use tui::{
  20. backend::Backend,
  21. layout::{Constraint, Direction, Layout, Rect},
  22. style::{Color, Modifier, Style},
  23. text::{Span, Spans},
  24. widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
  25. Frame,
  26. };
  27. use darkfi::util::time::NanoTimestamp;
  28. use crate::{
  29. error::{DnetViewError, DnetViewResult},
  30. model::{NodeInfo, SelectableObject},
  31. };
  32. //use log::debug;
  33. type MsgLog = Vec<(NanoTimestamp, String, String)>;
  34. type MsgMap = FxHashMap<String, MsgLog>;
  35. #[derive(Debug, Clone)]
  36. pub struct View {
  37. pub id_menu: IdMenu,
  38. pub msg_list: MsgList,
  39. pub selectables: FxHashMap<String, SelectableObject>,
  40. pub ordered_list: Vec<String>,
  41. }
  42. impl Default for View {
  43. fn default() -> Self {
  44. Self::new()
  45. }
  46. }
  47. impl<'a> View {
  48. pub fn new() -> Self {
  49. let msg_map = FxHashMap::default();
  50. let msg_list = MsgList::new(msg_map, 0);
  51. let selectables = FxHashMap::default();
  52. let id_menu = IdMenu::new(Vec::new());
  53. let ordered_list = Vec::new();
  54. Self { id_menu, msg_list, selectables, ordered_list }
  55. }
  56. pub fn update(&mut self, msg_map: MsgMap, selectables: FxHashMap<String, SelectableObject>) {
  57. self.update_selectable(selectables.clone());
  58. self.update_msg_list(msg_map);
  59. self.update_id_menu(selectables);
  60. self.update_msg_index();
  61. self.make_ordered_list();
  62. }
  63. fn update_id_menu(&mut self, selectables: FxHashMap<String, SelectableObject>) {
  64. for id in selectables.keys() {
  65. if !self.id_menu.ids.iter().any(|i| i == id) {
  66. self.id_menu.ids.push(id.to_string());
  67. }
  68. }
  69. }
  70. fn update_selectable(&mut self, selectables: FxHashMap<String, SelectableObject>) {
  71. for (id, obj) in selectables {
  72. self.selectables.insert(id, obj);
  73. }
  74. }
  75. fn make_ordered_list(&mut self) {
  76. for obj in self.selectables.values() {
  77. match obj {
  78. SelectableObject::Node(node) => {
  79. if !self.ordered_list.iter().any(|i| i == &node.id) {
  80. self.ordered_list.push(node.id.clone());
  81. }
  82. if !node.is_offline {
  83. for session in &node.children {
  84. if !session.is_empty {
  85. if !self.ordered_list.iter().any(|i| i == &session.id) {
  86. self.ordered_list.push(session.id.clone());
  87. }
  88. for connection in &session.children {
  89. if !self.ordered_list.iter().any(|i| i == &connection.id) {
  90. self.ordered_list.push(connection.id.clone());
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. SelectableObject::Lilith(lilith) => {
  98. if !self.ordered_list.iter().any(|i| i == &lilith.id) {
  99. self.ordered_list.push(lilith.id.clone());
  100. }
  101. for network in &lilith.networks {
  102. if !self.ordered_list.iter().any(|i| i == &network.id) {
  103. self.ordered_list.push(network.id.clone());
  104. }
  105. }
  106. }
  107. _ => (),
  108. }
  109. }
  110. //debug!(target: "dnetview", "render_ids()::ordered_list: {:?}", self.ordered_list);
  111. }
  112. // TODO: this function is dynamically resizing the msgs index
  113. // according to what set of msgs is selected.
  114. // it's ugly. would prefer something more simple
  115. fn update_msg_index(&mut self) {
  116. if let Some(sel) = self.id_menu.state.selected() {
  117. if let Some(ord) = self.ordered_list.get(sel) {
  118. if let Some(i) = self.msg_list.msg_map.get(ord) {
  119. self.msg_list.index = i.len();
  120. }
  121. }
  122. }
  123. }
  124. fn update_msg_list(&mut self, msg_map: MsgMap) {
  125. for (id, msg) in msg_map {
  126. self.msg_list.msg_map.insert(id, msg);
  127. }
  128. }
  129. pub fn render<B: Backend>(&mut self, f: &mut Frame<'_, B>) -> DnetViewResult<()> {
  130. let margin = 2;
  131. let direction = Direction::Horizontal;
  132. let cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)];
  133. let slice = Layout::default()
  134. .direction(direction)
  135. .margin(margin)
  136. .constraints(cnstrnts)
  137. .split(f.size());
  138. self.render_ids(f, slice.clone())?;
  139. if self.ordered_list.is_empty() {
  140. // we have not received any data
  141. Ok(())
  142. } else {
  143. // get the id at the current index
  144. match self.id_menu.state.selected() {
  145. Some(i) => {
  146. //debug!(target: "dnetview", "render()::selected index: {}", i);
  147. match self.ordered_list.get(i) {
  148. Some(i) => {
  149. let id = i.clone();
  150. self.render_info(f, slice, id)?;
  151. Ok(())
  152. }
  153. None => Err(DnetViewError::NoIdAtIndex),
  154. }
  155. }
  156. // nothing is selected right now
  157. None => Ok(()),
  158. }
  159. }
  160. }
  161. fn render_ids<B: Backend>(
  162. &mut self,
  163. f: &mut Frame<'_, B>,
  164. slice: Vec<Rect>,
  165. ) -> DnetViewResult<()> {
  166. let style = Style::default();
  167. let mut nodes = Vec::new();
  168. for obj in self.selectables.values() {
  169. match obj {
  170. SelectableObject::Node(node) => {
  171. if node.is_offline {
  172. let style = Style::default().fg(Color::Blue).add_modifier(Modifier::ITALIC);
  173. let mut name = String::new();
  174. name.push_str(&node.name);
  175. name.push_str("(Offline)");
  176. let name_span = Span::styled(name, style);
  177. let lines = vec![Spans::from(name_span)];
  178. let names = ListItem::new(lines);
  179. nodes.push(names);
  180. } else {
  181. let name_span = Span::raw(&node.name);
  182. let lines = vec![Spans::from(name_span)];
  183. let names = ListItem::new(lines);
  184. nodes.push(names);
  185. for session in &node.children {
  186. if !session.is_empty {
  187. let name = Span::styled(format!(" {}", session.name), style);
  188. let lines = vec![Spans::from(name)];
  189. let names = ListItem::new(lines);
  190. nodes.push(names);
  191. for connection in &session.children {
  192. let mut info = Vec::new();
  193. match connection.addr.as_str() {
  194. "Null" => {
  195. let style = Style::default()
  196. .fg(Color::Blue)
  197. .add_modifier(Modifier::ITALIC);
  198. let name = Span::styled(
  199. format!(" {} ", connection.addr),
  200. style,
  201. );
  202. info.push(name);
  203. }
  204. addr => {
  205. let name = Span::styled(
  206. format!(
  207. " {} ({})",
  208. addr, connection.remote_node_id
  209. ),
  210. style,
  211. );
  212. info.push(name);
  213. }
  214. }
  215. let lines = vec![Spans::from(info)];
  216. let names = ListItem::new(lines);
  217. nodes.push(names);
  218. }
  219. }
  220. }
  221. }
  222. }
  223. SelectableObject::Lilith(lilith) => {
  224. let name_span = Span::raw(&lilith.name);
  225. let lines = vec![Spans::from(name_span)];
  226. let names = ListItem::new(lines);
  227. nodes.push(names);
  228. for network in &lilith.networks {
  229. let name = Span::styled(format!(" {}", network.name), style);
  230. let lines = vec![Spans::from(name)];
  231. let names = ListItem::new(lines);
  232. nodes.push(names);
  233. }
  234. }
  235. _ => (),
  236. }
  237. }
  238. let nodes =
  239. List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> ");
  240. f.render_stateful_widget(nodes, slice[0], &mut self.id_menu.state);
  241. Ok(())
  242. }
  243. fn parse_msg_list(&self, connect_id: String) -> DnetViewResult<List<'a>> {
  244. let send_style = Style::default().fg(Color::LightCyan);
  245. let recv_style = Style::default().fg(Color::DarkGray);
  246. let mut texts = Vec::new();
  247. let mut lines = Vec::new();
  248. let log = self.msg_list.msg_map.get(&connect_id);
  249. match log {
  250. Some(values) => {
  251. for (i, (t, k, v)) in values.iter().enumerate() {
  252. lines.push(match k.as_str() {
  253. "send" => {
  254. Span::styled(format!("{} {} S: {}", i, t, v), send_style)
  255. }
  256. "recv" => {
  257. Span::styled(format!("{} {} R: {}", i, t, v), recv_style)
  258. }
  259. data => return Err(DnetViewError::UnexpectedData(data.to_string())),
  260. });
  261. }
  262. }
  263. None => return Err(DnetViewError::CannotFindId),
  264. }
  265. for line in lines.clone() {
  266. let text = ListItem::new(line);
  267. texts.push(text);
  268. }
  269. let msg_list = List::new(texts).block(Block::default().borders(Borders::ALL));
  270. Ok(msg_list)
  271. }
  272. fn render_info<B: Backend>(
  273. &mut self,
  274. f: &mut Frame<'_, B>,
  275. slice: Vec<Rect>,
  276. selected: String,
  277. ) -> DnetViewResult<()> {
  278. let style = Style::default();
  279. let mut lines = Vec::new();
  280. if self.selectables.is_empty() {
  281. // we have not received any selectable data
  282. return Ok(())
  283. } else {
  284. let info = self.selectables.get(&selected);
  285. //debug!(target: "dnetview", "render_info()::selected {}", selected);
  286. match info {
  287. Some(SelectableObject::Node(node)) => {
  288. //debug!(target: "dnetview", "render_info()::SelectableObject::Node");
  289. lines.push(Spans::from(Span::styled("Type: Normal", style)));
  290. match &node.external_addr {
  291. Some(addr) => {
  292. let node_info = Span::styled(format!("External addr: {}", addr), style);
  293. lines.push(Spans::from(node_info));
  294. }
  295. None => {
  296. let node_info = Span::styled("External addr: Null".to_string(), style);
  297. lines.push(Spans::from(node_info));
  298. }
  299. }
  300. lines.push(Spans::from(Span::styled(
  301. format!("P2P state: {}", node.state),
  302. style,
  303. )));
  304. }
  305. Some(SelectableObject::Session(session)) => {
  306. //debug!(target: "dnetview", "render_info()::SelectableObject::Session");
  307. if session.accept_addr.is_some() {
  308. let accept_addr = Span::styled(
  309. format!("Accept addr: {}", session.accept_addr.as_ref().unwrap()),
  310. style,
  311. );
  312. lines.push(Spans::from(accept_addr));
  313. }
  314. if session.hosts.is_some() {
  315. let hosts = Span::styled("Hosts:".to_string(), style);
  316. lines.push(Spans::from(hosts));
  317. for host in session.hosts.as_ref().unwrap() {
  318. let host = Span::styled(format!(" {}", host), style);
  319. lines.push(Spans::from(host));
  320. }
  321. }
  322. }
  323. Some(SelectableObject::Connect(connect)) => {
  324. //debug!(target: "dnetview", "render_info()::SelectableObject::Connect");
  325. let text = self.parse_msg_list(connect.id.clone())?;
  326. f.render_stateful_widget(text, slice[1], &mut self.msg_list.state);
  327. }
  328. Some(SelectableObject::Lilith(lilith)) => {
  329. lines.push(Spans::from(Span::styled("Type: Lilith", style)));
  330. lines.push(Spans::from(Span::styled("URLs:", style)));
  331. for url in &lilith.urls {
  332. lines.push(Spans::from(Span::styled(format!(" {}", url), style)));
  333. }
  334. }
  335. Some(SelectableObject::Network(network)) => {
  336. lines.push(Spans::from(Span::styled("URLs:", style)));
  337. for url in &network.urls {
  338. lines.push(Spans::from(Span::styled(format!(" {}", url), style)));
  339. }
  340. lines.push(Spans::from(Span::styled("Hosts:", style)));
  341. for node in &network.nodes {
  342. lines.push(Spans::from(Span::styled(format!(" {}", node), style)));
  343. }
  344. }
  345. None => return Err(DnetViewError::NotSelectableObject),
  346. }
  347. }
  348. let graph = Paragraph::new(lines)
  349. .block(Block::default().borders(Borders::ALL))
  350. .style(Style::default());
  351. f.render_widget(graph, slice[1]);
  352. Ok(())
  353. }
  354. }
  355. #[derive(Debug, Clone)]
  356. pub struct IdMenu {
  357. pub state: ListState,
  358. pub ids: Vec<String>,
  359. }
  360. impl IdMenu {
  361. pub fn new(ids: Vec<String>) -> IdMenu {
  362. IdMenu { state: ListState::default(), ids }
  363. }
  364. pub fn next(&mut self) {
  365. let i = match self.state.selected() {
  366. Some(i) => {
  367. if i >= self.ids.len() - 1 {
  368. 0
  369. } else {
  370. i + 1
  371. }
  372. }
  373. None => 0,
  374. };
  375. self.state.select(Some(i));
  376. }
  377. pub fn previous(&mut self) {
  378. let i = match self.state.selected() {
  379. Some(i) => {
  380. if i == 0 {
  381. self.ids.len() - 1
  382. } else {
  383. i - 1
  384. }
  385. }
  386. None => 0,
  387. };
  388. self.state.select(Some(i));
  389. }
  390. pub fn unselect(&mut self) {
  391. self.state.select(None);
  392. }
  393. }
  394. #[derive(Debug, Clone)]
  395. pub struct MsgList {
  396. pub state: ListState,
  397. pub msg_map: MsgMap,
  398. pub index: usize,
  399. }
  400. impl MsgList {
  401. pub fn new(msg_map: MsgMap, index: usize) -> MsgList {
  402. MsgList { state: ListState::default(), msg_map, index }
  403. }
  404. // TODO: reimplement
  405. //pub fn next(&mut self) {
  406. // let i = match self.state.selected() {
  407. // Some(i) => {
  408. // if i >= self.msg_len - 1 {
  409. // 0
  410. // } else {
  411. // i + 1
  412. // }
  413. // }
  414. // None => 0,
  415. // };
  416. // self.state.select(Some(i));
  417. //}
  418. //pub fn previous(&mut self) {
  419. // let i = match self.state.selected() {
  420. // Some(i) => {
  421. // if i == 0 {
  422. // self.msg_len - 1
  423. // } else {
  424. // i - 1
  425. // }
  426. // }
  427. // None => 0,
  428. // };
  429. // self.state.select(Some(i));
  430. //}
  431. pub fn scroll(&mut self) -> DnetViewResult<()> {
  432. let i = match self.state.selected() {
  433. Some(i) => i + self.index,
  434. None => 0,
  435. };
  436. self.state.select(Some(i));
  437. Ok(())
  438. }
  439. pub fn unselect(&mut self) {
  440. self.state.select(None);
  441. }
  442. }
  443. #[derive(Debug, Clone)]
  444. pub struct NodeInfoView {
  445. pub index: usize,
  446. pub infos: FxHashMap<String, NodeInfo>,
  447. }
  448. impl NodeInfoView {
  449. pub fn new(infos: FxHashMap<String, NodeInfo>) -> NodeInfoView {
  450. let index = 0;
  451. NodeInfoView { index, infos }
  452. }
  453. //pub fn next(&mut self) {
  454. // self.index = (self.index + 1) % self.infos.len();
  455. //}
  456. //pub fn previous(&mut self) {
  457. // if self.index > 0 {
  458. // self.index -= 1;
  459. // } else {
  460. // self.index = self.infos.len() - 1;
  461. // }
  462. //}
  463. }