From 5da8701978d7ee3f11260d13a63b42e33b9ae986 Mon Sep 17 00:00:00 2001 From: calcu1on Date: Sat, 5 Apr 2025 20:19:42 -0400 Subject: [PATCH] Better handling for icons, moving things to impl for weather. --- src/main.rs | 42 +++++++++++++++++++++------------ src/redsox.rs | 25 +++++++++++--------- src/weather.rs | 63 ++++++++++++++++++++++---------------------------- 3 files changed, 69 insertions(+), 61 deletions(-) diff --git a/src/main.rs b/src/main.rs index cf24856..daf49b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![cfg_attr(debug_assertions, allow(dead_code, unused_imports))] mod weather; mod redsox; +mod nerdfont; // use colored::Colorize; use tabled::{Table, Tabled}; use serde::{Deserialize, Serialize}; @@ -13,22 +14,30 @@ use tabled::settings::{ struct TableRow { date: String, time_of_day: String, - temp: i32, + temp: String, red_sox: String, forecast: String, } #[allow(unreachable_code)] fn main() { - // Set the weather location here. - let location = weather::WeatherOfficeLocation { + // Get forecast. + let entire_forecast: Vec = weather::WeatherOfficeLocation { x: 75, y: 59, code: "GYX".to_string(), - }; - let entire_forecast: Vec = weather::get_full_forecast(location); + }.get_full_forecast(); + // Get sox schedule. let sox_games: Vec = redsox::get_schedule(); - let baseball_diamond = '\u{f0852}'; + // Build icons. + let baseball_icon = nerdfont::NerdFontIcon { + icon_code: "f0852".to_string(), + }.get_icon().unwrap(); + let clock_icon = nerdfont::NerdFontIcon { + icon_code: "e641".to_string(), + }.get_icon().unwrap(); + + // Build the rows for the table. let mut table_rows: Vec = vec![]; for i in 0..entire_forecast.len() { let forecast_period = &entire_forecast[i]; @@ -37,17 +46,19 @@ fn main() { // Check if there is a sox game and print opp. for sox_game in &sox_games { if sox_game.date == yyyy_mm_dd { - let mut opp_str = String::from(baseball_diamond); - opp_str.push_str(" "); - opp_str.push_str(&sox_game.opponent); - sox_status = opp_str; + // @todo - currently hardcoding time - figure out how to get it. + sox_status = format!("{} {}\n{} {}", baseball_icon, &sox_game.opponent, clock_icon, "8:00".to_string()); break; } } + // Get fahrenheight icon; + let fahrenheight_icon = nerdfont::NerdFontIcon { + icon_code: "e341".to_string(), + }.get_icon().unwrap(); let row = TableRow { date: yyyy_mm_dd.to_string(), time_of_day: forecast_period.name.clone(), - temp: forecast_period.temperature, + temp: format!("{}{}", forecast_period.temperature, fahrenheight_icon), red_sox: sox_status, forecast: forecast_period.detailed_forecast.to_string(), }; @@ -59,12 +70,13 @@ fn main() { fn render_table(rows: &Vec) { // here is where we actually render the table. let mut table = Table::new(rows); - table.with(Style::modern()); + table.with((Style::modern(), Alignment::center())); + table.modify(Columns::last(), Alignment::left()); table.with(( - Width::wrap(210).priority(Priority::max(true)), - Width::increase(50).priority(Priority::min(true)), + Width::wrap(195).priority(Priority::max(true)), + Width::increase(60).priority(Priority::min(true)), )); - table.modify(Columns::first(), Alignment::right()); + table.modify(Columns::single(3), Alignment::left()); println!("{}", table); } diff --git a/src/redsox.rs b/src/redsox.rs index efc24da..ffce4c3 100644 --- a/src/redsox.rs +++ b/src/redsox.rs @@ -1,11 +1,9 @@ use serde::{Deserialize, Serialize}; use serde_alias::serde_alias; use std::fs; -use iso8601; +// use iso8601; -const SOX_ID: i32 = 111; -const SOX_SCHEDULE_URL: &str = "https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId=111&startDate=2025-04-01&endDate=2025-09-30"; -const SOX_SCHEDULE_LOCAL: &str = "/Users/danchadwick/Projects/rust/pulse/assets/mlb-response.json"; +const TEAM_ID: i32 = 111; #[serde_alias(CamelCase,SnakeCase)] #[derive(Serialize, Deserialize, Debug, Clone)] @@ -68,11 +66,9 @@ pub struct GameInfo { // Gets the full forecast from the response. pub fn get_schedule() -> Vec { let client = reqwest::blocking::Client::new(); - // let schedule_json: String = fs::read_to_string(SOX_SCHEDULE_LOCAL).expect("Unable to read file").to_owned(); - let schedule_json: String = client.get(SOX_SCHEDULE_URL).send().expect("Unable to get data").text().unwrap().to_string(); + let schedule_url = build_api_url(); + let schedule_json: String = client.get(&schedule_url).send().expect("Unable to get data").text().unwrap().to_string(); let schedule: Schedule = serde_json::from_str(&schedule_json).expect("JSON was not well-formatted"); - // Iterate over the schedule, extract datapoints to create new struct - // Return a vec of the new structs. let mut full_schedule: Vec = vec![]; let dates = schedule.dates; for date in dates { @@ -88,6 +84,7 @@ pub fn get_schedule() -> Vec { full_schedule } +// Determine who the opponent is from the teams. pub fn extract_opponent(teams: &Teams) -> String { if teams.home.team.name == "Boston Red Sox" { teams.away.team.name.to_string() @@ -97,10 +94,16 @@ pub fn extract_opponent(teams: &Teams) -> String { } } -#[cfg(test)] -mod sox_tests { - use super::*; +// Build the url for the api request. +fn build_api_url() -> String { + let url_first: String = "https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId=".to_string(); + let url_second: String= "&startDate=2025-04-01&endDate=2025-09-30".to_string(); + format!("{}{}{}", url_first, TEAM_ID, url_second) +} +#[cfg(test)] +mod team_tests { + use super::*; #[test] fn check_schedule_retrieval() { get_schedule(); diff --git a/src/weather.rs b/src/weather.rs index 91b30d8..59af9d2 100644 --- a/src/weather.rs +++ b/src/weather.rs @@ -33,6 +33,7 @@ pub struct WeatherOfficeLocation { } impl WeatherOfficeLocation { + // Build api request URL. pub fn build_url(&self) -> String { format!( "https://api.weather.gov/gridpoints/{}/{},{}/forecast", @@ -41,31 +42,30 @@ impl WeatherOfficeLocation { self.y, ) } -} - -// Gets the full forecast from the response. -pub fn get_full_forecast(location: WeatherOfficeLocation) -> Vec { - 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 ForecastWrapper { properties: Properties { mut periods } } = serde_json::from_str(&forecast).expect("JSON was not well-formatted"); - // let json: ForecastWrapper = serde_json::from_str(&forecast).expect("JSON was not well-formatted"); - // let mut weather_periods: Vec = json.properties.periods.into_iter().collect(); - for period in periods.iter_mut() { - match detect_icon(&period.short_forecast) { - None => println!("There was an issue detecting the correct icon!!!"), - Some(icon) => { - period.detailed_forecast = format!("{} {}", icon, &period.detailed_forecast); - } - }; + + // Get the full forecast for the location. + pub fn get_full_forecast(&self) -> Vec { + let url = WeatherOfficeLocation::build_url(&self); + 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 ForecastWrapper { properties: Properties { mut periods } } = serde_json::from_str(&forecast).expect("JSON was not well-formatted"); + for period in periods.iter_mut() { + match detect_icon(&period.short_forecast) { + None => println!("There was an issue detecting the correct icon!!!"), + Some(icon) => { + period.detailed_forecast = format!("{} {}", icon, &period.detailed_forecast); + } + }; + } + periods } - periods } +// Detect which icon to display based on short forecast. pub fn detect_icon(short_forecast: &String) -> Option { if short_forecast.contains("Sunny") { let icon = nerdfont::NerdFontIcon { @@ -73,45 +73,38 @@ pub fn detect_icon(short_forecast: &String) -> Option { }; let icon_code = icon.get_icon(); icon_code - } - else if short_forecast.contains("Rain") && short_forecast.contains("Snow") { + } 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") { + } 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") { + } 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") { + } else if short_forecast.contains("Cloudy") { let icon = nerdfont::NerdFontIcon { icon_code: "e312".to_string(), }; let icon_code = icon.get_icon(); icon_code - } - else if short_forecast.contains("Clear") { + } else if short_forecast.contains("Clear") { let icon = nerdfont::NerdFontIcon { icon_code: "e30d".to_string(), }; let icon_code = icon.get_icon(); icon_code - } - else { + } else { None } } -