Implementation of modules in separate structs
This commit is contained in:
parent
a20e1d8336
commit
e1f2518239
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "unibar"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1.0.196"
|
||||||
|
serde_json = "1.0.113"
|
||||||
|
curl = "0.4.46"
|
||||||
|
clap = { version = "4.5.1", features = ["derive"] }
|
44
src/bar_modules/bar_module_music.rs
Normal file
44
src/bar_modules/bar_module_music.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use std::process::{Stdio, Command};
|
||||||
|
|
||||||
|
use crate::common;
|
||||||
|
use crate::bar_modules;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UnibarModuleMusic {
|
||||||
|
pub opts: common::AppOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl bar_modules::BarModuleActions for UnibarModuleMusic {
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
fn get(&self) -> String {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
|
||||||
|
// MPD format options here:
|
||||||
|
// https://manpages.ubuntu.com/manpages/plucky/man1/mpc.1.html
|
||||||
|
let current_output = Command::new("mpc")
|
||||||
|
.arg("--format")
|
||||||
|
.arg("[%artist% - ][%title%]")
|
||||||
|
.arg("current")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
let mut current_stdout = String::from_utf8(current_output.stdout).unwrap();
|
||||||
|
current_stdout.pop();
|
||||||
|
parts.push(format!("{}", current_stdout));
|
||||||
|
|
||||||
|
if self.opts.music_progress {
|
||||||
|
let progress_output = Command::new("mpc")
|
||||||
|
.arg("status")
|
||||||
|
.arg("%percenttime% %totaltime%")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
let mut progress_stdout = String::from_utf8(progress_output.stdout).unwrap();
|
||||||
|
progress_stdout.pop();
|
||||||
|
parts.push(format!("[{}]", progress_stdout));
|
||||||
|
}
|
||||||
|
|
||||||
|
return format!("{}", parts.join(" "));
|
||||||
|
}
|
||||||
|
}
|
71
src/bar_modules/bar_module_weather.rs
Normal file
71
src/bar_modules/bar_module_weather.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use std::str;
|
||||||
|
use curl::easy::{Easy, List};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::common;
|
||||||
|
use crate::bar_modules;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UnibarModuleWeather {
|
||||||
|
pub opts: common::AppOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnibarModuleWeather {
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
fn get_current_temperature(&self, v: Value) -> f32 {
|
||||||
|
let deg_c :f32 = v["features"][0]["properties"]["temperature"]["value"]
|
||||||
|
.to_string().parse().unwrap();
|
||||||
|
|
||||||
|
match self.opts.weather_units {
|
||||||
|
common::TemperatureUnits::Metric => return deg_c,
|
||||||
|
common::TemperatureUnits::Imperial => return (deg_c * 9.0 / 5.0) + 32.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
fn get_temperature_unit(&self) -> String{
|
||||||
|
return if self.opts.weather_units == common::TemperatureUnits::Metric { "°C".to_string() }
|
||||||
|
else { "°F".to_string() };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl bar_modules::BarModuleActions for UnibarModuleWeather {
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
fn get(&self) -> String {
|
||||||
|
// Print a web page onto stdout
|
||||||
|
let mut curl = Easy::new();
|
||||||
|
let mut url_string = Vec::new();
|
||||||
|
let mut curl_ret = Vec::new();
|
||||||
|
|
||||||
|
url_string.push("https://api.weather.gov/stations/".to_owned());
|
||||||
|
url_string.push(self.opts.weather_station.to_owned());
|
||||||
|
url_string.push("/observations?limit=1".to_owned());
|
||||||
|
|
||||||
|
curl.url(url_string.concat().as_str()).unwrap();
|
||||||
|
{
|
||||||
|
let mut list = List::new();
|
||||||
|
list.append("User-Agent: Bar Weather (mahesh@heshapps.com)").unwrap();
|
||||||
|
curl.http_headers(list).unwrap();
|
||||||
|
|
||||||
|
let mut transfer = curl.transfer();
|
||||||
|
|
||||||
|
transfer.write_function(|data| {
|
||||||
|
curl_ret.extend_from_slice(data);
|
||||||
|
Ok(data.len())
|
||||||
|
}).unwrap();
|
||||||
|
transfer.perform().unwrap();
|
||||||
|
}
|
||||||
|
if self.opts.debug_json {
|
||||||
|
println!("-----> curl_data - [{}]", std::str::from_utf8(&curl_ret).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let v: Value = serde_json::from_str(str::from_utf8(&curl_ret).unwrap()).unwrap();
|
||||||
|
let temperature_value :f32 = self.get_current_temperature(v);
|
||||||
|
let temperature_unit :String = self.get_temperature_unit();
|
||||||
|
|
||||||
|
return format!("{:.2}{}", temperature_value, temperature_unit);
|
||||||
|
}
|
||||||
|
}
|
8
src/bar_modules/mod.rs
Normal file
8
src/bar_modules/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// --------------------
|
||||||
|
// All Bar modules must implement the actions
|
||||||
|
pub trait BarModuleActions {
|
||||||
|
fn get(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod bar_module_weather;
|
||||||
|
pub mod bar_module_music;
|
30
src/common/mod.rs
Normal file
30
src/common/mod.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Enums
|
||||||
|
// --------------------
|
||||||
|
#[derive(Debug,PartialEq,Eq,Copy,Clone)]
|
||||||
|
pub enum TemperatureUnits {
|
||||||
|
Metric,
|
||||||
|
Imperial,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TemperatureUnits {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
TemperatureUnits::Metric => write!(f, "Metric"),
|
||||||
|
TemperatureUnits::Imperial => write!(f, "Imperial"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Application options
|
||||||
|
// --------------------
|
||||||
|
#[derive(Debug,Clone)]
|
||||||
|
pub struct AppOptions {
|
||||||
|
pub weather_units: TemperatureUnits,
|
||||||
|
pub weather_station: String,
|
||||||
|
pub music_progress: bool,
|
||||||
|
pub debug_json: bool,
|
||||||
|
}
|
136
src/main.rs
136
src/main.rs
@ -1,27 +1,10 @@
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::fmt;
|
|
||||||
use std::process::{Stdio, Command};
|
|
||||||
use curl::easy::{Easy, List};
|
|
||||||
use serde_json::Value;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
//
|
// Common utilities/types
|
||||||
// Enums
|
mod common;
|
||||||
//
|
// Bar Modules
|
||||||
#[derive(Debug,PartialEq,Eq,Copy,Clone)]
|
mod bar_modules;
|
||||||
enum TemperatureUnits {
|
|
||||||
Metric,
|
|
||||||
Imperial,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TemperatureUnits {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
TemperatureUnits::Metric => write!(f, "Metric"),
|
|
||||||
TemperatureUnits::Imperial => write!(f, "Imperial"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commandline parsing
|
// Commandline parsing
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -39,28 +22,22 @@ struct CommandlineArgs {
|
|||||||
#[arg(short = 'm', long)]
|
#[arg(short = 'm', long)]
|
||||||
weather_metric: bool,
|
weather_metric: bool,
|
||||||
|
|
||||||
|
/// Show music progess
|
||||||
|
#[arg(short = 'p', long)]
|
||||||
|
music_progress: bool,
|
||||||
|
|
||||||
/// Show JSON data returned by query
|
/// Show JSON data returned by query
|
||||||
#[arg(short = 'D', long)]
|
#[arg(short = 'D', long)]
|
||||||
debug_json: bool,
|
debug_json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Application options
|
|
||||||
//
|
|
||||||
#[derive(Debug,Clone)]
|
|
||||||
struct AppOptions {
|
|
||||||
units: TemperatureUnits,
|
|
||||||
weather_station: String,
|
|
||||||
debug_json: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Application (Unibar)
|
// Application (Unibar)
|
||||||
//
|
//
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Unibar {
|
struct Unibar {
|
||||||
// acts :UnibarActions
|
// acts :UnibarActions
|
||||||
opts :AppOptions,
|
opts: common::AppOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unibar {
|
impl Unibar {
|
||||||
@ -70,72 +47,17 @@ impl Unibar {
|
|||||||
self.debug_msg("Debugging ...");
|
self.debug_msg("Debugging ...");
|
||||||
}
|
}
|
||||||
self.check_options();
|
self.check_options();
|
||||||
let mut bar_modules = Vec::new();
|
let mut parts = Vec::new();
|
||||||
|
|
||||||
bar_modules.push(self.clone().get_weather());
|
let bar_modules_enabled: Vec<Box<dyn bar_modules::BarModuleActions>> = vec! [
|
||||||
bar_modules.push(self.clone().get_music());
|
Box::new(bar_modules::bar_module_weather::UnibarModuleWeather { opts: self.opts.clone() }),
|
||||||
|
Box::new(bar_modules::bar_module_music::UnibarModuleMusic { opts: self.opts.clone() }),
|
||||||
|
];
|
||||||
|
|
||||||
println!("{}", bar_modules.join(" | "));
|
for md in bar_modules_enabled {
|
||||||
}
|
parts.push(md.get());
|
||||||
|
|
||||||
// --------------------
|
|
||||||
fn get_weather(self) -> String {
|
|
||||||
// Print a web page onto stdout
|
|
||||||
let mut curl = Easy::new();
|
|
||||||
let mut url_string = Vec::new();
|
|
||||||
let mut curl_ret = Vec::new();
|
|
||||||
|
|
||||||
url_string.push("https://api.weather.gov/stations/".to_owned());
|
|
||||||
url_string.push(self.opts.weather_station.to_owned());
|
|
||||||
url_string.push("/observations?limit=1".to_owned());
|
|
||||||
|
|
||||||
curl.url(url_string.concat().as_str()).unwrap();
|
|
||||||
{
|
|
||||||
let mut list = List::new();
|
|
||||||
list.append("User-Agent: Bar Weather (mahesh@heshapps.com)").unwrap();
|
|
||||||
curl.http_headers(list).unwrap();
|
|
||||||
|
|
||||||
let mut transfer = curl.transfer();
|
|
||||||
|
|
||||||
transfer.write_function(|data| {
|
|
||||||
curl_ret.extend_from_slice(data);
|
|
||||||
Ok(data.len())
|
|
||||||
}).unwrap();
|
|
||||||
transfer.perform().unwrap();
|
|
||||||
}
|
}
|
||||||
if self.opts.debug_json {
|
println!("{}", parts.join(" | "));
|
||||||
println!("-----> curl_data - [{}]", std::str::from_utf8(&curl_ret).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
let v: Value = serde_json::from_str(str::from_utf8(&curl_ret).unwrap()).unwrap();
|
|
||||||
let temperature_value :f32 = self.get_current_temperature(v);
|
|
||||||
let temperature_unit :String = self.get_temperature_unit();
|
|
||||||
|
|
||||||
return format!("{:.2}{}", temperature_value, temperature_unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
fn get_music(&self) -> String {
|
|
||||||
// MPD format options here:
|
|
||||||
// https://manpages.ubuntu.com/manpages/plucky/man1/mpc.1.html
|
|
||||||
let current_output = Command::new("mpc")
|
|
||||||
.arg("--format")
|
|
||||||
.arg("[%artist% - ][%title%]")
|
|
||||||
.arg("current")
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
let mut current_stdout = String::from_utf8(current_output.stdout).unwrap();
|
|
||||||
current_stdout.pop();
|
|
||||||
let progress_output = Command::new("mpc")
|
|
||||||
.arg("status")
|
|
||||||
.arg("%percenttime% %totaltime%")
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
let mut progress_stdout = String::from_utf8(progress_output.stdout).unwrap();
|
|
||||||
progress_stdout.pop();
|
|
||||||
return format!("{} [{}]", current_stdout, progress_stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
@ -148,23 +70,6 @@ impl Unibar {
|
|||||||
return all_good;
|
return all_good;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------
|
|
||||||
fn get_current_temperature(&self, v: Value) -> f32 {
|
|
||||||
let deg_c :f32 = v["features"][0]["properties"]["temperature"]["value"]
|
|
||||||
.to_string().parse().unwrap();
|
|
||||||
|
|
||||||
match self.opts.units {
|
|
||||||
TemperatureUnits::Metric => return deg_c,
|
|
||||||
TemperatureUnits::Imperial => return (deg_c * 9.0 / 5.0) + 32.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
fn get_temperature_unit(&self) -> String{
|
|
||||||
return if self.opts.units == TemperatureUnits::Metric { "°C".to_string() }
|
|
||||||
else { "°F".to_string() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
fn debug_msg(&self, msg: &str) {
|
fn debug_msg(&self, msg: &str) {
|
||||||
println!("[Debug ] -----> {}", msg);
|
println!("[Debug ] -----> {}", msg);
|
||||||
@ -177,10 +82,11 @@ impl Unibar {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let cmd_args = CommandlineArgs::parse();
|
let cmd_args = CommandlineArgs::parse();
|
||||||
let app = Unibar {
|
let app = Unibar {
|
||||||
opts: AppOptions {
|
opts: common::AppOptions {
|
||||||
units: if cmd_args.weather_metric { TemperatureUnits::Metric }
|
weather_units: if cmd_args.weather_metric { common::TemperatureUnits::Metric }
|
||||||
else { TemperatureUnits::Imperial },
|
else { common::TemperatureUnits::Imperial },
|
||||||
weather_station: cmd_args.weather_station,
|
weather_station: cmd_args.weather_station,
|
||||||
|
music_progress: cmd_args.music_progress,
|
||||||
debug_json: cmd_args.debug_json
|
debug_json: cmd_args.debug_json
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user