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)] #[derive(Clone)]
pub struct UnibarModuleMusic { 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 { impl bar_modules::BarModuleActions for UnibarModuleMusic {
// -------------------- // --------------------
fn get(&self) -> String { fn generate_data(&mut self) {
let mut parts = Vec::new();
// MPD format options here: // MPD format options here:
// https://manpages.ubuntu.com/manpages/plucky/man1/mpc.1.html // https://manpages.ubuntu.com/manpages/plucky/man1/mpc.1.html
let current_output = Command::new("mpc") let current_output = Command::new("mpc")
@ -23,9 +34,8 @@ impl bar_modules::BarModuleActions for UnibarModuleMusic {
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
.unwrap(); .unwrap();
let mut current_stdout = String::from_utf8(current_output.stdout).unwrap(); self.current_stdout = String::from_utf8(current_output.stdout).unwrap();
current_stdout.pop(); self.current_stdout.pop();
parts.push(format!("{}", current_stdout));
if self.opts.music_progress { if self.opts.music_progress {
let progress_output = Command::new("mpc") let progress_output = Command::new("mpc")
@ -34,11 +44,32 @@ impl bar_modules::BarModuleActions for UnibarModuleMusic {
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
.unwrap(); .unwrap();
let mut progress_stdout = String::from_utf8(progress_output.stdout).unwrap(); self.progress_stdout = String::from_utf8(progress_output.stdout).unwrap();
progress_stdout.pop(); self.progress_stdout.pop();
parts.push(format!("[{}]", progress_stdout)); }
}
// --------------------
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(" ")); 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 std::str;
use curl::easy::{Easy, List}; use curl::easy::{Easy, List};
use serde_json::json;
use serde_json::Value; use serde_json::Value;
use regex::Regex; use regex::Regex;
@ -8,15 +9,24 @@ use crate::bar_modules;
#[derive(Clone)] #[derive(Clone)]
pub struct UnibarModuleWeather { pub struct UnibarModuleWeather {
pub opts: common::AppOptions, opts: common::AppOptions,
weather_info: Value,
} }
impl UnibarModuleWeather { impl UnibarModuleWeather {
// -------------------- // --------------------
fn get_current_temperature(&self, v: Value) -> f32 { pub fn new(o :common::AppOptions) -> UnibarModuleWeather {
let deg_c :f32 = v["features"][0]["properties"]["temperature"]["value"] UnibarModuleWeather {
.to_string().parse().unwrap(); 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 { match self.opts.weather_units {
common::TemperatureUnits::Metric => return deg_c, common::TemperatureUnits::Metric => return deg_c,
@ -31,19 +41,8 @@ impl UnibarModuleWeather {
} }
// -------------------- // --------------------
fn get_icon(&self, v: Value) -> String { fn get_unicode_symbol(&self, condition: &str) -> String {
// "icon": "https://api.weather.gov/icons/land/night/ovc?size=medium", match condition {
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 {
"skc" => { return "☀️".to_string(); } // Fair/clear "skc" => { return "☀️".to_string(); } // Fair/clear
"few" => { return "🌥️".to_string(); } // A few clouds "few" => { return "🌥️".to_string(); } // A few clouds
"sct" => { return "🌥️".to_string(); } // Partly cloudy "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 // Print a web page onto stdout
let mut curl = Easy::new(); let mut curl = Easy::new();
let mut url_string = Vec::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!("{} skc Fair/clear", self.get_unicode_symbol("skc"));
println!("{} few A few clouds", self.get_unicode_symbol("few")); println!("{} few A few clouds", self.get_unicode_symbol("few"));
println!("{} sct Partly cloudy", self.get_unicode_symbol("sct")); println!("{} sct Partly cloudy", self.get_unicode_symbol("sct"));
@ -152,7 +151,7 @@ impl UnibarModuleWeather {
impl bar_modules::BarModuleActions for UnibarModuleWeather { impl bar_modules::BarModuleActions for UnibarModuleWeather {
// -------------------- // --------------------
fn get(&self) -> String { fn generate_data(&mut self) {
// Print a web page onto stdout // Print a web page onto stdout
let mut curl = Easy::new(); let mut curl = Easy::new();
let mut url_string = Vec::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()); println!("-----> curl_data - [{}]", std::str::from_utf8(&curl_ret).unwrap());
} }
let v: Value = serde_json::from_str(str::from_utf8(&curl_ret).unwrap()).unwrap(); self.weather_info = 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());
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 { 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; pub mod bar_module_weather;

View File

@ -27,5 +27,5 @@ pub struct AppOptions {
pub weather_station: String, pub weather_station: String,
pub music_progress: bool, pub music_progress: bool,
pub debug_json: 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 /// Show ICON debug information
#[arg(short = 'I', long)] #[arg(short = 'I', long)]
debug_icons: bool, debug_modules: bool,
} }
// //
@ -51,24 +51,45 @@ impl Unibar {
self.debug_msg("Debugging ..."); self.debug_msg("Debugging ...");
} }
self.check_options(); 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! [ 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_weather::UnibarModuleWeather::new(self.opts.clone())),
Box::new(bar_modules::bar_module_music::UnibarModuleMusic { opts: self.opts.clone() }), Box::new(bar_modules::bar_module_music::UnibarModuleMusic::new(self.opts.clone())),
]; ];
for md in bar_modules_enabled { // Get module's part to be displayed in the bar
parts.push(md.get()); 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(" | ")); 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, weather_station: cmd_args.weather_station,
music_progress: cmd_args.music_progress, music_progress: cmd_args.music_progress,
debug_json: cmd_args.debug_json, debug_json: cmd_args.debug_json,
debug_icons: cmd_args.debug_icons debug_modules: cmd_args.debug_modules
}, },
}; };