Skip to content

Commit

Permalink
Merge pull request #26 from robgonnella/rendering-refactor
Browse files Browse the repository at this point in the history
Rendering refactor
  • Loading branch information
robgonnella authored Sep 23, 2024
2 parents 410598b + 9dd92f5 commit ce38073
Show file tree
Hide file tree
Showing 21 changed files with 939 additions and 534 deletions.
36 changes: 26 additions & 10 deletions r-lanui/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::{collections::HashMap, env};

use serde::{Deserialize, Serialize};

use crate::ui::store::types::Theme;
use crate::ui::store::state::Theme;

pub const DEFAULT_CONFIG_ID: &str = "default";
pub const DEFAULT_PORTS_STR: &str = "22,80,443,2000-9999";
Expand All @@ -22,6 +22,9 @@ pub struct Config {
pub cidr: String,
pub theme: String,
pub ports: Vec<String>,
pub default_ssh_user: String,
pub default_ssh_port: String,
pub default_ssh_identity: String,
pub device_configs: HashMap<String, DeviceConfig>,
}

Expand All @@ -33,12 +36,19 @@ pub fn get_default_ports() -> Vec<String> {
}

impl Config {
pub fn new() -> Self {
pub fn default() -> Self {
let user = env::var("USER").unwrap();
let home = env::var("HOME").unwrap();
let identity = format!("{home}/.ssh/id_rsa");

Self {
id: DEFAULT_CONFIG_ID.to_string(),
theme: Theme::Blue.to_string(),
cidr: "unknown".to_string(),
ports: get_default_ports(),
default_ssh_identity: identity,
default_ssh_port: String::from("22"),
default_ssh_user: user,
device_configs: HashMap::new(),
}
}
Expand All @@ -50,34 +60,40 @@ pub struct ConfigManager {
}

impl ConfigManager {
pub fn new(path: String) -> Self {
pub fn new(path: &str) -> Self {
let f: Result<std::fs::File, std::io::Error> = std::fs::File::open(&path);

match f {
Ok(file) => {
let configs: HashMap<String, Config> = serde_yaml::from_reader(file).unwrap();
Self { path, configs }
Self {
path: String::from(path),
configs,
}
}
Err(_) => {
let default_conf = Config::new();
let default_conf = Config::default();
let mut configs: HashMap<String, Config> = HashMap::new();
configs.insert(default_conf.id.clone(), default_conf.clone());
let mut man = Self { path, configs };
let mut man = Self {
path: String::from(path),
configs,
};
man.write();
man
}
}
}

pub fn get_by_id(&self, id: &String) -> Option<Config> {
pub fn get_by_id(&self, id: &str) -> Option<Config> {
let c = self.configs.get(id);
match c {
Some(conf) => Some(conf.clone()),
None => None,
}
}

pub fn get_by_cidr(&self, cidr: &String) -> Option<Config> {
pub fn get_by_cidr(&self, cidr: &str) -> Option<Config> {
let mut config: Option<Config> = None;

self.configs.iter().for_each(|(_, c)| {
Expand All @@ -95,7 +111,7 @@ impl ConfigManager {
self.write();
}

pub fn update_theme(&mut self, id: &String, theme: &Theme) {
pub fn update_theme(&mut self, id: &str, theme: &Theme) {
if let Some(conf) = self.configs.get_mut(id) {
conf.theme = theme.clone().to_string();
self.write();
Expand Down
9 changes: 4 additions & 5 deletions r-lanui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use directories::ProjectDirs;
use log::*;
use simplelog;
use std::{
collections::{HashMap, HashSet},
collections::HashSet,
env, fs,
sync::{
mpsc::{self, Receiver, Sender},
Arc, Mutex,
},
thread::{self, JoinHandle},
};
use ui::store::{action::Action, dispatcher::Dispatcher, types::Theme};
use ui::store::{action::Action, dispatcher::Dispatcher};

use r_lanlib::{
network::{self, NetworkInterface},
Expand Down Expand Up @@ -249,7 +249,7 @@ fn monitor_network(

fn init(args: &Args, interface: &NetworkInterface) -> (Config, Arc<Dispatcher>) {
let config_path = get_project_config_path();
let config_manager = Arc::new(Mutex::new(ConfigManager::new(config_path)));
let config_manager = Arc::new(Mutex::new(ConfigManager::new(&config_path)));
let dispatcher = Arc::new(Dispatcher::new(Arc::clone(&config_manager)));

let manager = config_manager.lock().unwrap();
Expand All @@ -265,9 +265,8 @@ fn init(args: &Args, interface: &NetworkInterface) -> (Config, Arc<Dispatcher>)
config = Config {
id: fakeit::animal::animal().to_lowercase(),
cidr: interface.cidr.clone(),
device_configs: HashMap::new(),
ports: args.ports.clone(),
theme: Theme::Blue.to_string(),
..Config::default()
};
dispatcher.dispatch(Action::CreateAndSetConfig(&config))
}
Expand Down
63 changes: 29 additions & 34 deletions r-lanui/src/ui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,54 @@ use core::time;
use log::*;
use ratatui::{
backend::Backend,
crossterm::event::{self, Event, KeyCode},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
prelude::CrosstermBackend,
Terminal,
};
use std::{collections::HashMap, io, sync::Arc};
use std::{io, sync::Arc};

use super::{
store::{dispatcher::Dispatcher, types::ViewName},
views::{config::ConfigView, device::DeviceView, devices::DevicesView, View},
store::dispatcher::Dispatcher,
views::{main::MainView, View},
};

struct App {
dispatcher: Arc<Dispatcher>,
views: HashMap<ViewName, Box<dyn View>>,
main_view: Box<dyn View>,
}

impl App {
fn new(dispatcher: Arc<Dispatcher>) -> Self {
let mut views: HashMap<ViewName, Box<dyn View>> = HashMap::new();

views.insert(
ViewName::Config,
Box::new(ConfigView::new(Arc::clone(&dispatcher))),
);
views.insert(
ViewName::Device,
Box::new(DeviceView::new(Arc::clone(&dispatcher))),
);
views.insert(
ViewName::Devices,
Box::new(DevicesView::new(Arc::clone(&dispatcher))),
);

Self { dispatcher, views }
}

fn get_view(&mut self) -> &mut Box<dyn View> {
let view_name = self.dispatcher.get_state().view;
self.views.get_mut(&view_name).unwrap()
Self {
main_view: Box::new(MainView::new(dispatcher)),
}
}
}

pub fn launch(dispatcher: Arc<Dispatcher>) -> Result<(), Report> {
// setup terminal
let mut terminal = ratatui::init();
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;

let mut app = App::new(dispatcher);

// start app loop
let res = run_app(&mut terminal, &mut app);

// restore terminal
ratatui::restore();
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;

if let Err(err) = res {
error!("{err:?}");
Expand All @@ -64,16 +61,14 @@ pub fn launch(dispatcher: Arc<Dispatcher>) -> Result<(), Report> {

fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
loop {
let view = app.get_view();

terminal.draw(|f| view.render_ref(f.area(), f.buffer_mut()))?;
terminal.draw(|f| app.main_view.render_ref(f.area(), f.buffer_mut()))?;

// Use poll here so we don't block the thread, this will allow
// rendering of incoming device data from network as it's received
if let Ok(has_event) = event::poll(time::Duration::from_secs(1)) {
if let Ok(has_event) = event::poll(time::Duration::from_millis(60)) {
if has_event {
let evt = event::read()?;
let handled = view.process_event(&evt);
let handled = app.main_view.process_event(&evt);

if !handled {
match evt {
Expand Down
1 change: 1 addition & 0 deletions r-lanui/src/ui/components.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod device_info;
pub mod field;
pub mod footer;
pub mod header;
pub mod scrollbar;
Expand Down
67 changes: 20 additions & 47 deletions r-lanui/src/ui/components/device_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,37 @@ use ratatui::{
};

use crate::{
config::{Config, DeviceConfig},
ui::store::store::Colors,
config::DeviceConfig,
ui::{store::state::State, views::CustomWidget},
};

pub struct DeviceInfo<'c> {
pub struct DeviceInfo {
device: DeviceWithPorts,
_device_config: DeviceConfig,
config: Config,
colors: &'c Colors,
}

impl<'c> DeviceInfo<'c> {
pub fn new(
device: DeviceWithPorts,
device_config: DeviceConfig,
config: Config,
colors: &'c Colors,
) -> Self {
impl DeviceInfo {
pub fn new(device: DeviceWithPorts, device_config: DeviceConfig) -> Self {
Self {
device,
_device_config: device_config,
config,
colors,
}
}
}

impl<'c> Widget for DeviceInfo<'c> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
impl CustomWidget for DeviceInfo {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer, state: &State)
where
Self: Sized,
{
let spacer1 = Line::from("");
let spacer2 = Line::from("");
let spacer3 = Line::from("");

let hostname = Line::from(format!(" Hostname: {0}", self.device.hostname));
let ip = Line::from(format!(" IP: {0}", self.device.ip));
let mac = Line::from(format!(" MAC: {0}", self.device.mac));
let vendor = Line::from(format!(" Vendor: {0}", self.device.vendor));

let scanned_ports = Line::from(format!(
" Scanned Ports: {0}",
self.config.ports.join(", ")
));

let hostname = Line::from(format!("Hostname: {0}", self.device.hostname));
let ip = Line::from(format!("IP: {0}", self.device.ip));
let mac = Line::from(format!("MAC: {0}", self.device.mac));
let vendor = Line::from(format!("Vendor: {0}", self.device.vendor));
let open_ports = Line::from(format!(
" Open Ports: {0}",
"Open Ports: {0}",
self.device
.open_ports
.iter()
Expand All @@ -65,24 +48,14 @@ impl<'c> Widget for DeviceInfo<'c> {
.join(", ")
));

let info = Paragraph::new(vec![
spacer1,
hostname,
ip,
mac,
vendor,
spacer2,
scanned_ports,
spacer3,
open_ports,
])
.style(
Style::new()
.fg(self.colors.row_fg)
.bg(self.colors.buffer_bg),
)
.wrap(Wrap { trim: true })
.left_aligned();
let info = Paragraph::new(vec![spacer1, hostname, ip, mac, vendor, open_ports])
.style(
Style::new()
.fg(state.colors.row_fg)
.bg(state.colors.buffer_bg),
)
.wrap(Wrap { trim: true })
.left_aligned();

info.render(area, buf)
}
Expand Down
31 changes: 31 additions & 0 deletions r-lanui/src/ui/components/field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use ratatui::{
layout::Rect,
text::{Line, Span},
widgets::{Paragraph, Widget, Wrap},
};

use crate::ui::{store::state::State, views::CustomWidget};

pub struct Field {
key: String,
value: String,
}

impl Field {
pub fn new(key: String, value: String) -> Self {
Self { key, value }
}
}

impl CustomWidget for Field {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer, _state: &State)
where
Self: Sized,
{
let key = Span::from(format!("{0}: ", self.key));
let value = Span::from(self.value);
let line = Line::from(vec![key, value]);
let field = Paragraph::new(line).wrap(Wrap { trim: true });
field.render(area, buf)
}
}
Loading

0 comments on commit ce38073

Please sign in to comment.