Initial verison of implementation
This commit is contained in:
		
							
								
								
									
										191
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
				
			|||||||
 | 
					use std::str;
 | 
				
			||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					use std::process::{Stdio, Command};
 | 
				
			||||||
 | 
					use curl::easy::{Easy, List};
 | 
				
			||||||
 | 
					use serde_json::Value;
 | 
				
			||||||
 | 
					use clap::Parser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Enums
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#[derive(Debug,PartialEq,Eq,Copy,Clone)]
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					#[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 {
 | 
				
			||||||
 | 
					    /// 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 JSON data returned by query
 | 
				
			||||||
 | 
					    #[arg(short = 'D', long)]
 | 
				
			||||||
 | 
					    debug_json: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Application options
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#[derive(Debug,Clone)]
 | 
				
			||||||
 | 
					struct AppOptions {
 | 
				
			||||||
 | 
					    units: TemperatureUnits,
 | 
				
			||||||
 | 
					    weather_station: String,
 | 
				
			||||||
 | 
					    debug_json: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Application (Unibar)
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					struct Unibar {
 | 
				
			||||||
 | 
					    // acts    :UnibarActions
 | 
				
			||||||
 | 
					    opts :AppOptions,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Unibar {
 | 
				
			||||||
 | 
					    // --------------------
 | 
				
			||||||
 | 
					    fn run(&self) {
 | 
				
			||||||
 | 
					        if self.opts.debug_json {
 | 
				
			||||||
 | 
					            self.debug_msg("Debugging ...");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.check_options();
 | 
				
			||||||
 | 
					        let mut bar_modules = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bar_modules.push(self.clone().get_weather());
 | 
				
			||||||
 | 
					        bar_modules.push(self.clone().get_music());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        println!("{}", bar_modules.join(" | "));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --------------------
 | 
				
			||||||
 | 
					    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!("-----> 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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --------------------
 | 
				
			||||||
 | 
					    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 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) {
 | 
				
			||||||
 | 
					        println!("[Debug ] -----> {}", msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Entry point
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let cmd_args = CommandlineArgs::parse();
 | 
				
			||||||
 | 
					    let app = Unibar {
 | 
				
			||||||
 | 
					        opts: AppOptions {
 | 
				
			||||||
 | 
					            units: if cmd_args.weather_metric { TemperatureUnits::Metric }
 | 
				
			||||||
 | 
					                                         else { TemperatureUnits::Imperial },
 | 
				
			||||||
 | 
					            weather_station: cmd_args.weather_station,
 | 
				
			||||||
 | 
					            debug_json: cmd_args.debug_json
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.run();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// References:
 | 
				
			||||||
 | 
					// https://reintech.io/blog/working-with-json-in-rust
 | 
				
			||||||
		Reference in New Issue
	
	Block a user