/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::{fs::File, io, io::Read}; use async_std::sync::Arc; use clap::Parser; use easy_parallel::Parallel; use log::{debug, info}; use ratatui::{ backend::{Backend, TermionBackend}, Terminal, }; use simplelog::*; use smol::Executor; use termion::{async_stdin, event::Key, input::TermRead, raw::IntoRawMode}; use darkfi::util::{ async_util, cli::{get_log_config, get_log_level, spawn_config, Config}, path::{expand_path, get_config_path}, }; pub mod config; pub mod error; pub mod model; pub mod options; pub mod parser; pub mod rpc; pub mod util; pub mod view; use crate::{ config::{DnvConfig, CONFIG_FILE, CONFIG_FILE_CONTENTS}, error::{DnetViewError, DnetViewResult}, model::Model, options::Args, parser::DataParser, view::View, }; struct DnetView { model: Arc, view: View, } impl DnetView { fn new(model: Arc, view: View) -> Self { Self { model, view } } async fn render_view(&mut self, terminal: &mut Terminal) -> DnetViewResult<()> { let mut asi = async_stdin(); terminal.clear()?; self.view.id_menu.state.select(Some(0)); self.view.msg_list.state.select(Some(0)); loop { self.view.update( self.model.msg_map.lock().await.clone(), self.model.selectables.lock().await.clone(), ); let mut err: Option = None; terminal.draw(|f| match self.view.render(f) { Ok(()) => {} Err(e) => { err = Some(e); } })?; if let Some(e) = err { return Err(e) } self.view.msg_list.scroll()?; for k in asi.by_ref().keys() { match k.unwrap() { Key::Char('q') => { terminal.clear()?; return Ok(()) } Key::Char('j') => { self.view.id_menu.next(); } Key::Char('k') => { self.view.id_menu.previous(); } Key::Char('u') => { // TODO //view.msg_list.next(); } Key::Char('d') => { // TODO //view.msg_list.previous(); } _ => (), } } async_util::msleep(100).await; } } } #[async_std::main] async fn main() -> DnetViewResult<()> { let args = Args::parse(); let log_level = get_log_level(args.verbose); let log_config = get_log_config(args.verbose); let log_file_path = expand_path(&args.log_path)?; if let Some(parent) = log_file_path.parent() { std::fs::create_dir_all(parent)?; }; let file = File::create(log_file_path)?; WriteLogger::init(log_level, log_config, file)?; info!("Log level: {}", log_level); let config_path = get_config_path(args.config, CONFIG_FILE)?; spawn_config(&config_path, CONFIG_FILE_CONTENTS)?; let config = Config::::load(config_path)?; let stdout = io::stdout().into_raw_mode()?; let backend = TermionBackend::new(stdout); let mut terminal = Terminal::new(backend)?; terminal.clear()?; let model = Model::new(); let view = View::new(); let ex = Arc::new(Executor::new()); let ex2 = ex.clone(); let mut dnetview = DnetView::new(model.clone(), view); let parser = DataParser::new(model, config); let nthreads = std::thread::available_parallelism().unwrap().get(); let (signal, shutdown) = async_channel::unbounded::<()>(); let (_, result) = Parallel::new() .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv()))) .finish(|| { smol::future::block_on(async move { parser.start_connect_slots(ex2).await?; dnetview.render_view(&mut terminal).await?; drop(signal); Ok(()) }) }); result }