view.rs 18 KB

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