use std::str; use std::thread; use std::time::Duration; use clap::Parser; use crate::common::UpdateResult; // Common utilities/types mod common; // Bar Modules mod bar_modules; // Commandline parsing #[derive(Parser, Debug)] #[command(name = "unibar")] #[command(version = "1.0")] #[command(about = "Get string of info for a status bar")] #[command(about, long_about = "A tool that returns variety of components in a string suitable to use in a status bar")] struct CommandlineArgs { /// Update interval in seconds #[arg(short = 'i', long, default_value = "1")] interval: u64, /// Name of the weather station #[arg(short = 's', long, default_value = "khio")] weather_station: String, /// Use Metric units in weather data. Imperial units otherwise #[arg(short = 'm', long)] weather_metric: bool, /// Show music progess #[arg(short = 'p', long)] music_progress: bool, /// Show verbose debug information during run #[arg(short = 'D', long)] debug: bool, /// Show module debug information after all modules are evaluated /// but before output is printed #[arg(short = 'M', long)] debug_modules: bool, /// Show module debug information after all modules are evaluated /// but before output is printed #[arg(short = 'U', long)] debug_update: bool, } // // Application (Unibar) // //#[derive(Clone)] struct Unibar { opts: common::AppOptions, bar_modules_enabled: Vec>, debug_modules_done: bool, } impl Unibar { // -------------------- fn run(&mut self) { if self.opts.debug { self.debug_msg("Debugging ..."); } self.check_options(); // Set up a list of all modules to be used self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_music::UnibarModuleMusic::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_memory::UnibarModuleMemory::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_cpu::UnibarModuleCpu::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_power::UnibarModulePower::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_network::UnibarModuleNetwork::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_weather::UnibarModuleWeather::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_date::UnibarModuleDate::new(self.opts.clone()))); self.bar_modules_enabled.push(Box::new(bar_modules::bar_module_time::UnibarModuleTime::new(self.opts.clone()))); // Run in a forever-loop ... loop { // ... to update the bar contents ... self.update_bar_contents(); // ... at specfied interval thread::sleep(Duration::from_millis(self.opts.interval * 1000)); } } // -------------------- fn update_bar_contents(&mut self) { // Get module's part to be displayed in the bar let mut parts = Vec::new(); for md in &mut self.bar_modules_enabled { let mut mod_parts = Vec::new(); // Each bar module implements following 4 steps: // * Clear data from previous iteration // * 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 match md.should_update() { UpdateResult::Update => { md.clear(); md.generate_data(); } UpdateResult::Skip => { if self.opts.debug_update { println!("Skipping module {}", md.get_name()); } } } mod_parts.push(md.get_icon()); mod_parts.push(md.get_content()); parts.push(mod_parts.join(" ")); } self.show_module_debug(); // Print parts provided by each module println!("status {}", parts.join(" ")); } // -------------------- fn show_module_debug(&mut self) { // Show module debug information if enabled if self.opts.debug_modules { if !self.debug_modules_done { let bar_modules_debugged: Vec> = 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())), Box::new(bar_modules::bar_module_memory::UnibarModuleMemory::new(self.opts.clone())), Box::new(bar_modules::bar_module_network::UnibarModuleNetwork::new(self.opts.clone())), Box::new(bar_modules::bar_module_cpu::UnibarModuleCpu::new(self.opts.clone())), Box::new(bar_modules::bar_module_power::UnibarModulePower::new(self.opts.clone())), Box::new(bar_modules::bar_module_date::UnibarModuleDate::new(self.opts.clone())), Box::new(bar_modules::bar_module_time::UnibarModuleTime::new(self.opts.clone())), ]; for md in bar_modules_debugged { md.post_debug(); } self.debug_modules_done = true; } } } // -------------------- fn check_options(&self) -> bool { let all_good = true; // If there are option checks to be added, make all_good a // mutable var and update its status return all_good; } // -------------------- fn debug_msg(&self, msg: &str) { println!("[Debug ] -----> {}", msg); } } // // Entry point // fn main() { let cmd_args = CommandlineArgs::parse(); let mut app = Unibar { opts: common::AppOptions { interval: cmd_args.interval as u64, weather_units: if cmd_args.weather_metric { common::TemperatureUnits::Metric } else { common::TemperatureUnits::Imperial }, weather_station: cmd_args.weather_station, music_progress: cmd_args.music_progress, debug: cmd_args.debug, debug_modules: cmd_args.debug_modules, debug_update: cmd_args.debug_update, }, bar_modules_enabled: Vec::new(), debug_modules_done: false, }; app.run(); } // References: // https://reintech.io/blog/working-with-json-in-rust