Lots of changes and reorgnizing things.
This commit is contained in:
		
							parent
							
								
									8e1ae39164
								
							
						
					
					
						commit
						2d936f6272
					
				
							
								
								
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -89,6 +89,15 @@ version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "colored" | ||||
| version = "3.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" | ||||
| dependencies = [ | ||||
|  "windows-sys 0.59.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "convert_case" | ||||
| version = "0.4.0" | ||||
| @ -802,6 +811,7 @@ dependencies = [ | ||||
| name = "pulse" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "colored", | ||||
|  "reqwest", | ||||
|  "serde", | ||||
|  "serde_alias", | ||||
|  | ||||
| @ -4,6 +4,7 @@ version = "0.1.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
| [dependencies] | ||||
| colored = "3.0.0" | ||||
| reqwest = { version = "0.12.15", features = ["blocking"] } | ||||
| serde = {version = "1.0.219", features = ["derive"]} | ||||
| serde_alias = "0.0.2" | ||||
|  | ||||
							
								
								
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,12 +1,11 @@ | ||||
| #![cfg_attr(debug_assertions, allow(dead_code, unused_imports))] | ||||
| mod weather; | ||||
| mod redsox; | ||||
| use tabled::Table; | ||||
| use tabled::{Table, Tabled}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use tabled::settings::{ | ||||
|     peaker::Priority, Width, Style, Alignment, object::Columns | ||||
| }; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use tabled::Tabled; | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Tabled)] | ||||
| #[tabled(rename_all = "UPPERCASE")] | ||||
| @ -21,7 +20,6 @@ struct TableRow { | ||||
| #[allow(unreachable_code)] | ||||
| fn main() { | ||||
|     let baseball_diamond = '\u{f15ec}'; | ||||
|     let sunny = '\u{f0599}'; | ||||
|     // Set the weather location here.
 | ||||
|     let location = weather::WeatherOfficeLocation { | ||||
|         x: 75, | ||||
| @ -48,19 +46,17 @@ fn main() { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         let mut forecast_w_icon = String::new(); | ||||
|         forecast_w_icon.push(sunny); | ||||
|         forecast_w_icon.push_str(" "); | ||||
|         forecast_w_icon.push_str(&item.detailed_forecast); | ||||
|         let row = TableRow { | ||||
|             date: date.to_string(), | ||||
|             time_of_day: item.name.clone(), | ||||
|             temp: item.temperature, | ||||
|             red_sox: sox_status, | ||||
|             forecast: forecast_w_icon, | ||||
|             forecast: item.detailed_forecast.to_string(), | ||||
|         }; | ||||
|         table_rows.push(row); | ||||
|     } | ||||
| 
 | ||||
|     // here is where we actually render the table.
 | ||||
|     let mut table = Table::new(table_rows); | ||||
|     table.with(Style::modern()); | ||||
|     table.with(( | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/nerdfont.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/nerdfont.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| pub struct NerdFontIcon { | ||||
|   pub icon_code: String, | ||||
| } 
 | ||||
| 
 | ||||
| impl NerdFontIcon { | ||||
|     /// Get the single font by name.
 | ||||
|     pub fn get_icon(&self) -> Option<char> { | ||||
|         u32::from_str_radix(&self.icon_code, 16).ok().and_then(char::from_u32) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										123
									
								
								src/weather.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/weather.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| use reqwest::header::USER_AGENT; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_alias::serde_alias; | ||||
| #[path = "nerdfont.rs"] mod nerdfont; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| struct ForecastWrapper { | ||||
|     properties: Properties, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| struct Properties { | ||||
|     periods: Vec<WeatherPeriod>, | ||||
| } | ||||
| 
 | ||||
| #[serde_alias(CamelCase,SnakeCase)] | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct WeatherPeriod { | ||||
|     pub name: String, | ||||
|     pub temperature: u64, | ||||
|     pub wind_direction: String, | ||||
|     pub wind_speed: String, | ||||
|     pub detailed_forecast: String, | ||||
|     pub short_forecast: String, | ||||
|     pub start_time: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct WeatherOfficeLocation { | ||||
|     pub x: i32, | ||||
|     pub y: i32, | ||||
|     pub code: String, | ||||
| } | ||||
| 
 | ||||
| impl WeatherOfficeLocation { | ||||
|     pub fn build_url(&self) -> String { | ||||
|         format!( | ||||
|             "https://api.weather.gov/gridpoints/{}/{},{}/forecast", | ||||
|             self.code, | ||||
|             self.x, | ||||
|             self.y, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Gets the full forecast from the response.
 | ||||
| pub fn get_full_forecast(location: WeatherOfficeLocation) -> Vec<WeatherPeriod> { | ||||
|     let url = WeatherOfficeLocation::build_url(&location); | ||||
|     let client = reqwest::blocking::Client::new(); | ||||
|     let forecast = client.get(&url) | ||||
|         .header(USER_AGENT, "My SuperAwesome Weather App") | ||||
|         .send() | ||||
|         .expect("Unable to get data") | ||||
|         .text().unwrap().to_string(); | ||||
|     let json: ForecastWrapper = serde_json::from_str(&forecast).expect("JSON was not well-formatted"); | ||||
|     let weather_periods: Vec<WeatherPeriod> = json.properties.periods.into_iter().collect(); | ||||
|     let icon_forecasts = enhance_forecasts(&weather_periods); 
 | ||||
| 
 | ||||
|     icon_forecasts | ||||
| } | ||||
| 
 | ||||
| pub fn enhance_forecasts(periods: &Vec<WeatherPeriod>) -> Vec<WeatherPeriod> { | ||||
|     let mut rebuilt_periods: Vec<WeatherPeriod> = vec![]; | ||||
|     for period in periods { | ||||
|         let icon = detect_icon(&period.short_forecast).unwrap(); | ||||
|         let icon_forecast = format!("{} {}", icon, &period.detailed_forecast); | ||||
|         let rebuilt_period = WeatherPeriod { | ||||
|             name: period.name.to_string(), | ||||
|             temperature: period.temperature, | ||||
|             wind_direction: period.wind_direction.to_string(), | ||||
|             wind_speed: period.wind_speed.to_string(), | ||||
|             detailed_forecast: icon_forecast, | ||||
|             short_forecast: period.short_forecast.to_string(), | ||||
|             start_time: period.start_time.to_string(),     
 | ||||
|         }; | ||||
|         rebuilt_periods.push(rebuilt_period); | ||||
|     } | ||||
| 
 | ||||
|     rebuilt_periods | ||||
| } | ||||
| 
 | ||||
| pub fn detect_icon(short_forecast: &String) -> Option<char> { | ||||
|     if short_forecast.contains("Sunny") { | ||||
|         let icon = nerdfont::NerdFontIcon { | ||||
|             icon_code: "f0599".to_string(), | ||||
|         }; | ||||
|         let icon_code = icon.get_icon(); | ||||
|         icon_code | ||||
|     } 
 | ||||
|     else if short_forecast.contains("Rain") && short_forecast.contains("Snow") { | ||||
|         let icon = nerdfont::NerdFontIcon { | ||||
|             icon_code: "f067f".to_string(), | ||||
|         }; | ||||
|         let icon_code = icon.get_icon(); | ||||
|         icon_code | ||||
|     } | ||||
|     else if short_forecast.contains("Snow") { | ||||
|         let icon = nerdfont::NerdFontIcon { | ||||
|             icon_code: "f0f36".to_string(), | ||||
|         }; | ||||
|         let icon_code = icon.get_icon(); | ||||
|         icon_code | ||||
|     } | ||||
|     else if short_forecast.contains("Rain") { | ||||
|         let icon = nerdfont::NerdFontIcon { | ||||
|             icon_code: "e239".to_string(), | ||||
|         }; | ||||
|         let icon_code = icon.get_icon(); | ||||
|         icon_code | ||||
|     } | ||||
|     else if short_forecast.contains("Cloudy") { | ||||
|         let icon = nerdfont::NerdFontIcon { | ||||
|             icon_code: "e312".to_string(), | ||||
|         }; | ||||
|         let icon_code = icon.get_icon(); | ||||
|         icon_code | ||||
|     } | ||||
|     else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1,54 +0,0 @@ | ||||
| use reqwest::header::USER_AGENT; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_alias::serde_alias; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct ForecastWrapper { | ||||
|     properties: Properties, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| struct Properties { | ||||
|     periods: Vec<WeatherPeriod>, | ||||
| } | ||||
| 
 | ||||
| #[serde_alias(CamelCase,SnakeCase)] | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct WeatherPeriod { | ||||
|     pub name: String, | ||||
|     pub temperature: u64, | ||||
|     pub wind_direction: String, | ||||
|     pub wind_speed: String, | ||||
|     pub detailed_forecast: String, | ||||
|     pub start_time: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct WeatherOfficeLocation { | ||||
|     pub x: i32, | ||||
|     pub y: i32, | ||||
|     pub code: String, | ||||
| } | ||||
| 
 | ||||
| impl WeatherOfficeLocation { | ||||
|     pub fn build_url(&self) -> String { | ||||
|         format!( | ||||
|             "https://api.weather.gov/gridpoints/{}/{},{}/forecast", | ||||
|             self.code, | ||||
|             self.x, | ||||
|             self.y | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Gets the full forecast from the response.
 | ||||
| pub fn get_full_forecast(location: WeatherOfficeLocation) -> Vec<WeatherPeriod> { | ||||
|     let url = WeatherOfficeLocation::build_url(&location); | ||||
|     let client = reqwest::blocking::Client::new(); | ||||
|     let forecast = client.get(&url).header(USER_AGENT, "My SuperAwesome Weather App").send().expect("Unable to get data").text().unwrap().to_string(); | ||||
|     let json: ForecastWrapper = serde_json::from_str(&forecast).expect("JSON was not well-formatted"); | ||||
|     let periods: Vec<WeatherPeriod> = json.properties.periods.into_iter().collect(); | ||||
| 
 | ||||
|     periods | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										12
									
								
								todo.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								todo.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| # Todo | ||||
| - [ ] weather: Parse each period and detect type of forecast | ||||
| - [ ] weather: add colors to each icon for forecast | ||||
| (sunny = yellow) :: (rainy = blue) :: (cloudy = gray) | ||||
| - [ ] redsox: Only show one game per date  | ||||
| - [ ] main: clean things up in the function | ||||
| 
 | ||||
| ## Notes | ||||
| ### Detecting weather forecast type | ||||
| Need to change the default return from get_full_forecast. | ||||
| It should be a DATE => [forecast1, forecast2] | ||||
| where each forecast has an icon associated with is | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 calcu1on
						calcu1on