diff --git a/src/checker/actuator.rs b/src/checker/actuator.rs new file mode 100644 index 0000000..8494e8b --- /dev/null +++ b/src/checker/actuator.rs @@ -0,0 +1,45 @@ +use crate::checker::{CheckResult, CheckState}; +use crate::config::CheckConfig; +use serde::Deserialize; + +#[derive(Deserialize)] +struct ActuatorResponse { + status: String, +} + +pub struct Checker<'a> { + check_config: &'a CheckConfig, +} + +impl Checker<'_> { + pub fn new(check_config: &CheckConfig) -> Checker { + Checker { check_config } + } + + pub async fn check(&self) -> CheckResult { + let state = match reqwest::get(self.check_config.url.as_str()).await { + Ok(r) => { + if r.status().is_success() { + match r.json::().await { + Ok(ar) => { + if ar.status == "UP" { + CheckState::Up + } else { + CheckState::Warn + } + } + _ => CheckState::Warn, + } + } else { + CheckState::Warn + } + } + Err(_) => CheckState::Down, + }; + + CheckResult { + name: self.check_config.name.to_string(), + state, + } + } +} diff --git a/src/checker/http.rs b/src/checker/http.rs new file mode 100644 index 0000000..02063b5 --- /dev/null +++ b/src/checker/http.rs @@ -0,0 +1,30 @@ +use crate::checker::{CheckResult, CheckState}; +use crate::config::CheckConfig; + +pub struct Checker<'a> { + check_config: &'a CheckConfig, +} + +impl Checker<'_> { + pub fn new(check_config: &CheckConfig) -> Checker { + Checker { check_config } + } + + pub async fn check(&self) -> CheckResult { + let state = match reqwest::get(self.check_config.url.as_str()).await { + Ok(r) => { + if r.status().is_success() { + CheckState::Up + } else { + CheckState::Warn + } + } + Err(_) => CheckState::Down, + }; + + CheckResult { + name: self.check_config.name.to_string(), + state, + } + } +} diff --git a/src/checker/mod.rs b/src/checker/mod.rs new file mode 100644 index 0000000..daa7e1e --- /dev/null +++ b/src/checker/mod.rs @@ -0,0 +1,51 @@ +mod actuator; +mod http; +mod tcp; + +pub use crate::checker::actuator::Checker as ActuatorChecker; +pub use crate::checker::http::Checker as HttpChecker; +pub use crate::checker::tcp::Checker as TcpChecker; +use crate::config; +use crate::config::get_config; +use serde_json::json; +use std::fmt::{Display, Formatter, Result}; + +pub struct CheckResult { + pub name: String, + pub state: CheckState, +} + +impl Display for CheckResult { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let color_config = match get_config().colors { + Some(color_config) => color_config, + None => config::ColorConfig { + up: String::from("#00FF00"), + warn: String::from("#FFFF00"), + down: String::from("#FF0000"), + }, + }; + let color = match &self.state { + CheckState::Up => color_config.up, + CheckState::Warn => color_config.warn, + CheckState::Down => color_config.down, + }; + + write!( + f, + "{}", + json!({ + "full_text": self.name, + "name": self.name, + "separator_block_width": 16, + "color": color + }) + ) + } +} + +pub enum CheckState { + Up, + Warn, + Down, +} diff --git a/src/checker/tcp.rs b/src/checker/tcp.rs new file mode 100644 index 0000000..c5ab721 --- /dev/null +++ b/src/checker/tcp.rs @@ -0,0 +1,43 @@ +use std::str::FromStr; + +use reqwest::Url; +use tokio::net::TcpStream; + +use crate::checker::{CheckResult, CheckState}; +use crate::config::CheckConfig; + +pub struct Checker<'a> { + check_config: &'a CheckConfig, +} + +impl Checker<'_> { + pub fn new(check_config: &CheckConfig) -> Checker { + Checker { check_config } + } + + pub async fn check(&self) -> CheckResult { + if let Ok(url) = Url::from_str(self.check_config.url.as_str()) { + if url.scheme() == "tcp" && url.host_str().is_some() && url.port().is_some() { + let state = match TcpStream::connect(format!( + "{}:{}", + url.host_str().unwrap(), + url.port().unwrap() + )) + .await + { + Ok(_) => CheckState::Up, + _ => CheckState::Down, + }; + return CheckResult { + name: self.check_config.name.to_string(), + state, + }; + } + } + + CheckResult { + name: self.check_config.name.to_string(), + state: CheckState::Down, + } + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..6d9c760 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,59 @@ +use serde::Deserialize; +use std::{env, fs}; + +#[derive(Deserialize)] +pub struct Config { + pub interval: Option, + pub colors: Option, + pub checks: Vec, +} + +#[derive(Deserialize)] +pub struct ColorConfig { + pub up: String, + pub warn: String, + pub down: String, +} + +#[derive(Deserialize)] +pub struct CheckConfig { + pub name: String, + pub url: String, + pub check_type: Option, + pub click_cmd: Option, +} + +#[derive(Deserialize, PartialEq, Eq)] +pub enum CheckType { + Http, + Actuator, + Tcp, +} + +fn get_config_file() -> String { + match env::args().nth(1) { + Some(config_file) => config_file, + None => format!( + "{}/.checkbar.toml", + dirs::home_dir().unwrap().to_str().unwrap_or("") + ), + } +} + +pub fn get_config() -> Config { + match fs::read_to_string(get_config_file()) { + Ok(config) => match toml::from_str(config.as_str()) { + Ok(config) => config, + Err(_e) => Config { + interval: None, + colors: None, + checks: vec![], + }, + }, + Err(_) => Config { + interval: None, + colors: None, + checks: vec![], + }, + } +} diff --git a/src/main.rs b/src/main.rs index 4076499..330deac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,48 +1,15 @@ -use reqwest::Url; +mod checker; +mod config; + +use std::process; +use std::time::Duration; + use serde::Deserialize; use serde_json::json; -use std::env; -use std::fmt::{Display, Formatter, Result}; -use std::fs; -use std::process; -use std::str::FromStr; -use std::time::Duration; -use tokio::net::TcpStream; use tokio::task; -#[derive(Deserialize)] -struct Config { - interval: Option, - colors: Option, - checks: Vec, -} - -#[derive(Deserialize)] -struct ColorConfig { - up: String, - warn: String, - down: String, -} - -#[derive(Deserialize)] -struct CheckConfig { - name: String, - url: String, - check_type: Option, - click_cmd: Option, -} - -#[derive(Deserialize, PartialEq)] -enum CheckType { - Http, - Actuator, - Tcp, -} - -#[derive(Deserialize)] -struct ActuatorResponse { - status: String, -} +use crate::checker::{ActuatorChecker, CheckResult, HttpChecker, TcpChecker}; +use crate::config::{get_config, CheckConfig, CheckType}; #[derive(Deserialize)] struct ClickEvent { @@ -50,101 +17,11 @@ struct ClickEvent { button: u8, } -struct CheckResult { - name: String, - state: CheckState, -} - -impl Display for CheckResult { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let color_config = match get_config().colors { - Some(color_config) => color_config, - None => ColorConfig { - up: String::from("#00FF00"), - warn: String::from("#FFFF00"), - down: String::from("#FF0000"), - }, - }; - let color = match &self.state { - CheckState::Up => color_config.up, - CheckState::Warn => color_config.warn, - CheckState::Down => color_config.down, - }; - - write!( - f, - "{}", - json!({ - "full_text": self.name, - "name": self.name, - "separator_block_width": 16, - "color": color - }) - ) - } -} - -enum CheckState { - Up, - Warn, - Down, -} - async fn check_host(check_config: &CheckConfig) -> CheckResult { - if check_config.check_type == Some(CheckType::Tcp) { - if let Ok(url) = Url::from_str(check_config.url.as_str()) { - if url.scheme() == "tcp" { - if url.host_str().is_some() && url.port().is_some() { - let state = match TcpStream::connect(format!( - "{}:{}", - url.host_str().unwrap(), - url.port().unwrap() - )) - .await - { - Ok(_) => CheckState::Up, - _ => CheckState::Down, - }; - return CheckResult { - name: check_config.name.to_string(), - state, - }; - } - } - } - return CheckResult { - name: check_config.name.to_string(), - state: CheckState::Down, - }; - } - - let state = match reqwest::get(check_config.url.as_str()).await { - Ok(r) => { - if r.status().is_success() { - match check_config.check_type { - Some(CheckType::Actuator) => match r.json::().await { - Ok(ar) => { - if ar.status == "UP" { - CheckState::Up - } else { - CheckState::Warn - } - } - _ => CheckState::Warn, - }, - // Default: HTTP - _ => CheckState::Up, - } - } else { - CheckState::Warn - } - } - Err(_) => CheckState::Down, - }; - - CheckResult { - name: check_config.name.to_string(), - state, + match check_config.check_type { + Some(CheckType::Actuator) => ActuatorChecker::new(check_config).check().await, + Some(CheckType::Tcp) => TcpChecker::new(check_config).check().await, + _ => HttpChecker::new(check_config).check().await, } } @@ -163,34 +40,6 @@ async fn print_states(check_configs: &[CheckConfig]) { println!("{}],", entries.join(",")); } -fn get_config_file() -> String { - match env::args().nth(1) { - Some(config_file) => config_file, - None => format!( - "{}/.checkbar.toml", - dirs::home_dir().unwrap().to_str().unwrap_or("") - ), - } -} - -fn get_config() -> Config { - match fs::read_to_string(get_config_file()) { - Ok(config) => match toml::from_str(config.as_str()) { - Ok(config) => config, - Err(_e) => Config { - interval: None, - colors: None, - checks: vec![], - }, - }, - Err(_) => Config { - interval: None, - colors: None, - checks: vec![], - }, - } -} - async fn get_click_cmd(name: String) -> Option { for check in get_config().checks { if check.name == name {