Refactoring of bar modules

* Moved debug functions to separate trait
* Broke up module actions and moved assembling of icon and content
  in main instead of module itself. This will enable disabling of
  icon or content via options in the future
This commit is contained in:
Mahesh Asolkar 2025-05-11 16:02:14 -07:00
parent ade570b8af
commit b7f83b4810
Signed by: asolkar
GPG Key ID: 371CA8164433BDCC
5 changed files with 140 additions and 52 deletions

View File

@ -5,15 +5,26 @@ use crate::bar_modules;
#[derive(Clone)]
pub struct UnibarModuleMusic {
pub opts: common::AppOptions,
opts: common::AppOptions,
current_stdout :String,
progress_stdout :String,
}
impl UnibarModuleMusic {
// --------------------
pub fn new(o :common::AppOptions) -> UnibarModuleMusic {
UnibarModuleMusic {
opts: o,
current_stdout: "".to_string(),
progress_stdout: "".to_string(),
}
}
}
impl bar_modules::BarModuleActions for UnibarModuleMusic {
// --------------------
fn get(&self) -> String {
let mut parts = Vec::new();
fn generate_data(&mut self) {
// MPD format options here:
// https://manpages.ubuntu.com/manpages/plucky/man1/mpc.1.html
let current_output = Command::new("mpc")
@ -23,9 +34,8 @@ impl bar_modules::BarModuleActions for UnibarModuleMusic {
.stdout(Stdio::piped())
.output()
.unwrap();
let mut current_stdout = String::from_utf8(current_output.stdout).unwrap();
current_stdout.pop();
parts.push(format!("{}", current_stdout));
self.current_stdout = String::from_utf8(current_output.stdout).unwrap();
self.current_stdout.pop();
if self.opts.music_progress {
let progress_output = Command::new("mpc")
@ -34,11 +44,32 @@ impl bar_modules::BarModuleActions for UnibarModuleMusic {
.stdout(Stdio::piped())
.output()
.unwrap();
let mut progress_stdout = String::from_utf8(progress_output.stdout).unwrap();
progress_stdout.pop();
parts.push(format!("[{}]", progress_stdout));
self.progress_stdout = String::from_utf8(progress_output.stdout).unwrap();
self.progress_stdout.pop();
}
}
// --------------------
fn get_content(&self) -> String {
let mut parts = Vec::new();
parts.push(format!("{}", self.current_stdout));
if self.opts.music_progress {
parts.push(format!("[{}]", self.progress_stdout));
}
return format!("{}", parts.join(" "));
}
// --------------------
fn get_icon(&self) -> String {
return "".to_string();
}
}
impl bar_modules::BarModuleDebug for UnibarModuleMusic {
// --------------------
fn post_debug(&self) {
}
}

View File

@ -1,5 +1,6 @@
use std::str;
use curl::easy::{Easy, List};
use serde_json::json;
use serde_json::Value;
use regex::Regex;
@ -8,14 +9,23 @@ use crate::bar_modules;
#[derive(Clone)]
pub struct UnibarModuleWeather {
pub opts: common::AppOptions,
opts: common::AppOptions,
weather_info: Value,
}
impl UnibarModuleWeather {
// --------------------
fn get_current_temperature(&self, v: Value) -> f32 {
let deg_c :f32 = v["features"][0]["properties"]["temperature"]["value"]
pub fn new(o :common::AppOptions) -> UnibarModuleWeather {
UnibarModuleWeather {
opts: o,
weather_info: json!(serde_json::Value::Null),
}
}
// --------------------
fn get_current_temperature(&self) -> f32 {
let deg_c :f32 = self.weather_info["features"][0]["properties"]["temperature"]["value"]
.to_string().parse().unwrap();
match self.opts.weather_units {
@ -31,19 +41,8 @@ impl UnibarModuleWeather {
}
// --------------------
fn get_icon(&self, v: Value) -> String {
// "icon": "https://api.weather.gov/icons/land/night/ovc?size=medium",
let re = Regex::new(r"(\w+)\?size").unwrap();
let json_val = v["features"][0]["properties"]["icon"].to_string();
let caps = re.captures(&json_val).unwrap();
// println!("{}", caps.get(1).unwrap().as_str());
return self.get_unicode_symbol(caps.get(1).unwrap().as_str());
}
// --------------------
fn get_unicode_symbol(&self, v: &str) -> String {
match v {
fn get_unicode_symbol(&self, condition: &str) -> String {
match condition {
"skc" => { return "☀️".to_string(); } // Fair/clear
"few" => { return "🌥️".to_string(); } // A few clouds
"sct" => { return "🌥️".to_string(); } // Partly cloudy
@ -83,7 +82,7 @@ impl UnibarModuleWeather {
}
// --------------------
pub fn get_icons(&self) {
fn get_icons(&self) {
// Print a web page onto stdout
let mut curl = Easy::new();
let mut url_string = Vec::new();
@ -109,7 +108,7 @@ impl UnibarModuleWeather {
}
// --------------------
pub fn show_icons(&self) {
fn show_icons(&self) {
println!("{} skc Fair/clear", self.get_unicode_symbol("skc"));
println!("{} few A few clouds", self.get_unicode_symbol("few"));
println!("{} sct Partly cloudy", self.get_unicode_symbol("sct"));
@ -152,7 +151,7 @@ impl UnibarModuleWeather {
impl bar_modules::BarModuleActions for UnibarModuleWeather {
// --------------------
fn get(&self) -> String {
fn generate_data(&mut self) {
// Print a web page onto stdout
let mut curl = Easy::new();
let mut url_string = Vec::new();
@ -180,11 +179,35 @@ impl bar_modules::BarModuleActions for UnibarModuleWeather {
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.clone());
let temperature_unit :String = self.get_temperature_unit();
let temperature_icon :String = self.get_icon(v.clone());
self.weather_info = serde_json::from_str(str::from_utf8(&curl_ret).unwrap()).unwrap();
}
return format!("{} {:.2}{}", temperature_icon, temperature_value, temperature_unit);
// --------------------
fn get_content(&self) -> String {
let temperature_value :f32 = self.get_current_temperature();
let temperature_unit :String = self.get_temperature_unit();
// let temperature_icon :String = self.get_icon(v.clone());
return format!("{:.2}{}", temperature_value, temperature_unit);
}
// --------------------
fn get_icon(&self) -> String {
// "icon": "https://api.weather.gov/icons/land/night/ovc?size=medium",
let re = Regex::new(r"(\w+)\?size").unwrap();
let json_val = self.weather_info["features"][0]["properties"]["icon"].to_string();
let caps = re.captures(&json_val).unwrap();
// println!("{}", caps.get(1).unwrap().as_str());
return self.get_unicode_symbol(caps.get(1).unwrap().as_str());
}
}
impl bar_modules::BarModuleDebug for UnibarModuleWeather {
// --------------------
fn post_debug(&self) {
self.get_icons();
self.show_icons();
}
}

View File

@ -1,7 +1,20 @@
// --------------------
// All Bar modules must implement the actions
/// All Bar modules must implement the actions
pub trait BarModuleActions {
fn get(&self) -> String;
/// Do necessary processing to generate data for this bar module
fn generate_data(&mut self);
/// Return String content to be displayed in the bar
fn get_content(&self) -> String;
/// Return a Unicode icon to display before content in the bar.
/// This icon may differ based on content of the data
fn get_icon(&self) -> String;
}
pub trait BarModuleDebug {
/// Print debug information at the end
fn post_debug(&self);
}
pub mod bar_module_weather;

View File

@ -27,5 +27,5 @@ pub struct AppOptions {
pub weather_station: String,
pub music_progress: bool,
pub debug_json: bool,
pub debug_icons: bool,
pub debug_modules: bool,
}

View File

@ -32,7 +32,7 @@ struct CommandlineArgs {
/// Show ICON debug information
#[arg(short = 'I', long)]
debug_icons: bool,
debug_modules: bool,
}
//
@ -51,24 +51,45 @@ impl Unibar {
self.debug_msg("Debugging ...");
}
self.check_options();
let mut parts = Vec::new();
// Set up a list of all modules to be used
let bar_modules_enabled: Vec<Box<dyn bar_modules::BarModuleActions>> = vec! [
Box::new(bar_modules::bar_module_weather::UnibarModuleWeather { opts: self.opts.clone() }),
Box::new(bar_modules::bar_module_music::UnibarModuleMusic { opts: self.opts.clone() }),
Box::new(bar_modules::bar_module_weather::UnibarModuleWeather::new(self.opts.clone())),
Box::new(bar_modules::bar_module_music::UnibarModuleMusic::new(self.opts.clone())),
];
for md in bar_modules_enabled {
parts.push(md.get());
// Get module's part to be displayed in the bar
let mut parts = Vec::new();
for mut md in bar_modules_enabled {
let mut mod_parts = Vec::new();
// Each bar module implements following 3 steps:
// * Generate raw data with pertinent information
// * Return a unicode icon to be displayed
// * Return a String content to be displayed after the icon
//
// Following generates ICON+CONTENT string for a module to be displayed
// in the bar
md.generate_data();
mod_parts.push(md.get_icon());
mod_parts.push(md.get_content());
parts.push(mod_parts.join(" "));
}
// Show module debug information if enabled
if self.opts.debug_modules {
let bar_modules_debugged: Vec<Box<dyn bar_modules::BarModuleDebug>> = vec! [
Box::new(bar_modules::bar_module_weather::UnibarModuleWeather::new(self.opts.clone())),
Box::new(bar_modules::bar_module_music::UnibarModuleMusic::new(self.opts.clone())),
];
for md in bar_modules_debugged {
md.post_debug();
}
}
// Print parts provided by each module
println!("{}", parts.join(" | "));
if self.opts.debug_icons {
let w = bar_modules::bar_module_weather::UnibarModuleWeather { opts: self.opts.clone() };
w.get_icons();
w.show_icons();
}
}
// --------------------
@ -99,7 +120,7 @@ fn main() {
weather_station: cmd_args.weather_station,
music_progress: cmd_args.music_progress,
debug_json: cmd_args.debug_json,
debug_icons: cmd_args.debug_icons
debug_modules: cmd_args.debug_modules
},
};