Cleaned up imports a bit and added rustfmt.toml

This commit is contained in:
2023-08-08 10:50:04 -06:00
parent 0d4e283c21
commit 155675b596
6 changed files with 182 additions and 179 deletions
+10
View File
@@ -0,0 +1,10 @@
tab_spaces=2
edition = "2021"
reorder_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
reorder_modules = true
merge_derives = true
use_field_init_shorthand = true
format_macro_matchers = true
format_macro_bodies = true
+57 -61
View File
@@ -7,93 +7,89 @@ use crate::app::radarr::RadarrData;
use super::network::RadarrEvent; use super::network::RadarrEvent;
pub mod radarr;
pub(crate) mod key_binding; pub(crate) mod key_binding;
pub mod radarr;
#[derive(Debug)] #[derive(Debug)]
pub struct App { pub struct App {
network_tx: Option<Sender<RadarrEvent>>, network_tx: Option<Sender<RadarrEvent>>,
pub client: Client, pub client: Client,
pub title: &'static str, pub title: &'static str,
pub tick_until_poll: u64, pub tick_until_poll: u64,
pub tick_count: u64, pub tick_count: u64,
pub config: AppConfig, pub config: AppConfig,
pub data: Data, pub data: Data,
} }
impl App { impl App {
pub fn new( pub fn new(network_tx: Sender<RadarrEvent>, tick_until_poll: u64, config: AppConfig) -> Self {
network_tx: Sender<RadarrEvent>, App {
tick_until_poll: u64, network_tx: Some(network_tx),
config: AppConfig tick_until_poll,
) -> Self { config,
App { ..App::default()
network_tx: Some(network_tx), }
tick_until_poll, }
config,
..App::default() pub async fn dispatch(&mut self, action: RadarrEvent) {
} if let Some(network_tx) = &self.network_tx {
if let Err(e) = network_tx.send(action).await {
error!("Failed to send event. {:?}", e);
}
}
}
pub fn reset(&mut self) {
self.tick_count = 0;
// self.data = Data::default();
}
pub async fn on_tick(&mut self) {
if self.tick_count % self.tick_until_poll == 0 {
self.dispatch(RadarrEvent::GetOverview).await;
} }
pub async fn dispatch(&mut self, action: RadarrEvent) { self.tick_count += 1;
if let Some(network_tx) = &self.network_tx { }
if let Err(e) = network_tx.send(action).await {
error!("Failed to send event. {:?}", e);
}
}
}
pub fn reset(&mut self) {
self.tick_count = 0;
// self.data = Data::default();
}
pub async fn on_tick(&mut self) {
if self.tick_count % self.tick_until_poll == 0 {
self.dispatch(RadarrEvent::GetOverview).await;
}
self.tick_count += 1;
}
} }
impl Default for App { impl Default for App {
fn default() -> Self { fn default() -> Self {
App { App {
network_tx: None, network_tx: None,
client: Client::new(), client: Client::new(),
title: "DevTools", title: "DevTools",
tick_until_poll: 0, tick_until_poll: 0,
tick_count: 0, tick_count: 0,
config: AppConfig::default(), config: AppConfig::default(),
data: Data::default() data: Data::default(),
}
} }
}
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Data { pub struct Data {
pub radarr_data: RadarrData, pub radarr_data: RadarrData,
} }
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct AppConfig { pub struct AppConfig {
pub radarr: RadarrConfig, pub radarr: RadarrConfig,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct RadarrConfig { pub struct RadarrConfig {
pub host: String, pub host: String,
pub port: Option<u16>, pub port: Option<u16>,
pub api_token: String pub api_token: String,
} }
impl Default for RadarrConfig { impl Default for RadarrConfig {
fn default() -> Self { fn default() -> Self {
RadarrConfig { RadarrConfig {
host: "localhost".to_string(), host: "localhost".to_string(),
port: Some(7878), port: Some(7878),
api_token: "".to_string() api_token: "".to_string(),
}
} }
} }
}
+32 -31
View File
@@ -9,44 +9,45 @@ use crossterm::event::Event as CrosstermEvent;
use crate::event::Key; use crate::event::Key;
pub enum InputEvent<T> { pub enum InputEvent<T> {
KeyEvent(T), KeyEvent(T),
Tick Tick,
} }
pub struct Events { pub struct Events {
_tx: Sender<InputEvent<Key>>, _tx: Sender<InputEvent<Key>>,
rx: Receiver<InputEvent<Key>> rx: Receiver<InputEvent<Key>>,
} }
impl Events { impl Events {
pub fn new() -> Self { pub fn new() -> Self {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let tick_rate: Duration = Duration::from_millis(250); let tick_rate: Duration = Duration::from_millis(250);
let event_tx = tx.clone(); let event_tx = tx.clone();
thread::spawn(move || { thread::spawn(move || {
let mut last_tick = Instant::now(); let mut last_tick = Instant::now();
loop { loop {
let timeout = tick_rate.checked_sub(last_tick.elapsed()) let timeout = tick_rate
.unwrap_or_else(|| Duration::from_secs(0)); .checked_sub(last_tick.elapsed())
if event::poll(timeout).unwrap() { .unwrap_or_else(|| Duration::from_secs(0));
if let CrosstermEvent::Key(key) = event::read().unwrap() { if event::poll(timeout).unwrap() {
let key = Key::from(key); if let CrosstermEvent::Key(key) = event::read().unwrap() {
event_tx.send(InputEvent::KeyEvent(key)).unwrap(); let key = Key::from(key);
} event_tx.send(InputEvent::KeyEvent(key)).unwrap();
} }
}
if last_tick.elapsed() >= tick_rate { if last_tick.elapsed() >= tick_rate {
event_tx.send(InputEvent::Tick).unwrap(); event_tx.send(InputEvent::Tick).unwrap();
last_tick = Instant::now(); last_tick = Instant::now();
} }
} }
}); });
Events { _tx: tx, rx } Events { _tx: tx, rx }
} }
pub fn next(&self) -> Result<InputEvent<Key>, mpsc::RecvError> { pub fn next(&self) -> Result<InputEvent<Key>, mpsc::RecvError> {
self.rx.recv() self.rx.recv()
} }
} }
+29 -26
View File
@@ -5,44 +5,47 @@ use crossterm::event::{KeyCode, KeyEvent};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Key { pub enum Key {
Char(char), Char(char),
Unknown Unknown,
} }
impl Display for Key { impl Display for Key {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self { match *self {
Key::Char(c) => write!(f, "<{}>", c), Key::Char(c) => write!(f, "<{}>", c),
_ => write!(f, "<{:?}>", self) _ => write!(f, "<{:?}>", self),
}
} }
}
} }
impl From<KeyEvent> for Key { impl From<KeyEvent> for Key {
fn from(key_event: KeyEvent) -> Self { fn from(key_event: KeyEvent) -> Self {
match key_event { match key_event {
KeyEvent { KeyEvent {
code: KeyCode::Char(c), code: KeyCode::Char(c),
.. ..
} => Key::Char(c), } => Key::Char(c),
_ => Key::Unknown _ => Key::Unknown,
}
} }
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use crate::event::key::Key; use crate::event::key::Key;
#[test] #[test]
fn test_key_formatter() { fn test_key_formatter() {
assert_eq!(format!("{}", Key::Char('q')), "<q>"); assert_eq!(format!("{}", Key::Char('q')), "<q>");
} }
#[test] #[test]
fn test_key_from() { fn test_key_from() {
assert_eq!(Key::from(KeyEvent::from(KeyCode::Char('q'))), Key::Char('q')) assert_eq!(
} Key::from(KeyEvent::from(KeyCode::Char('q'))),
} Key::Char('q')
)
}
}
+46 -53
View File
@@ -1,16 +1,17 @@
use std::io; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant};
use anyhow::Result; use anyhow::Result;
use clap::Parser; use clap::Parser;
use crossterm::execute; use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}; use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use log::{debug, info}; use log::{debug, info};
use tokio::sync::{mpsc, Mutex};
use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Receiver;
use tui::Terminal; use tokio::sync::{mpsc, Mutex};
use tui::backend::CrosstermBackend; use tui::backend::CrosstermBackend;
use tui::Terminal;
use utils::init_logging_config; use utils::init_logging_config;
@@ -29,78 +30,70 @@ mod utils;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Cli { struct Cli {}
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
log4rs::init_config(init_logging_config())?; log4rs::init_config(init_logging_config())?;
Cli::parse(); Cli::parse();
let config = confy::load("managarr", "config")?; let config = confy::load("managarr", "config")?;
let (sync_network_tx, sync_network_rx) = mpsc::channel(500); let (sync_network_tx, sync_network_rx) = mpsc::channel(500);
let app = Arc::new(Mutex::new(App::new( let app = Arc::new(Mutex::new(App::new(sync_network_tx, 5000 / 250, config)));
sync_network_tx,
5000 / 250,
config
)));
let app_nw = Arc::clone(&app); let app_nw = Arc::clone(&app);
std::thread::spawn(move || start_networking(sync_network_rx, &app_nw)); std::thread::spawn(move || start_networking(sync_network_rx, &app_nw));
info!("Checking if Radarr server is up and running..."); info!("Checking if Radarr server is up and running...");
app.lock().await.dispatch(RadarrEvent::HealthCheck).await; app.lock().await.dispatch(RadarrEvent::HealthCheck).await;
simple_ui(&app).await?; simple_ui(&app).await?;
Ok(()) Ok(())
} }
#[tokio::main] #[tokio::main]
async fn start_networking(mut network_rx: Receiver<RadarrEvent>, app: &Arc<Mutex<App>>) { async fn start_networking(mut network_rx: Receiver<RadarrEvent>, app: &Arc<Mutex<App>>) {
let network = Network::new(reqwest::Client::new(), app); let network = Network::new(reqwest::Client::new(), app);
while let Some(network_event) = network_rx.recv().await { while let Some(network_event) = network_rx.recv().await {
debug!("Received network event: {:?}", network_event); debug!("Received network event: {:?}", network_event);
network.handle_radarr_event(network_event).await; network.handle_radarr_event(network_event).await;
} }
} }
async fn simple_ui(app: &Arc<Mutex<App>>) -> Result<()> { async fn simple_ui(app: &Arc<Mutex<App>>) -> Result<()> {
let mut stdout = io::stdout(); let mut stdout = io::stdout();
enable_raw_mode()?; enable_raw_mode()?;
execute!(stdout, EnterAlternateScreen)?; execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend)?;
terminal.clear()?; terminal.clear()?;
terminal.hide_cursor()?; terminal.hide_cursor()?;
let input_events = Events::new(); let input_events = Events::new();
loop { loop {
let mut app = app.lock().await; let mut app = app.lock().await;
terminal.draw(|f| ui(f, &app))?; terminal.draw(|f| ui(f, &app))?;
match input_events.next()? { match input_events.next()? {
InputEvent::KeyEvent(key) => { InputEvent::KeyEvent(key) => {
if key == Key::Char('q') { if key == Key::Char('q') {
break; break;
}
}
InputEvent::Tick => {
app.on_tick().await
}
} }
}
InputEvent::Tick => app.on_tick().await,
} }
}
terminal.show_cursor()?; terminal.show_cursor()?;
disable_raw_mode()?; disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?; execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?; terminal.show_cursor()?;
Ok(()) Ok(())
} }
+8 -8
View File
@@ -9,18 +9,18 @@ pub(crate) mod radarr;
#[derive(Debug, Eq, PartialEq, Hash)] #[derive(Debug, Eq, PartialEq, Hash)]
pub enum RadarrEvent { pub enum RadarrEvent {
HealthCheck, HealthCheck,
GetOverview GetOverview,
} }
pub struct Network<'a> { pub struct Network<'a> {
pub client: Client, pub client: Client,
pub app: &'a Arc<Mutex<App>> pub app: &'a Arc<Mutex<App>>,
} }
impl<'a> Network<'a> { impl<'a> Network<'a> {
pub fn new(client: Client, app: &'a Arc<Mutex<App>>) -> Self { pub fn new(client: Client, app: &'a Arc<Mutex<App>>) -> Self {
Network { client, app } Network { client, app }
} }
} }