Switchin to icon enum, adding some basic tests.
This commit is contained in:
parent
bc974f44f4
commit
48cd4e6eb6
34
src/icons.rs
Normal file
34
src/icons.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum Icons {
|
||||||
|
Fahrenheight,
|
||||||
|
Clock,
|
||||||
|
Baseball,
|
||||||
|
Sunny,
|
||||||
|
Mixed,
|
||||||
|
Rain,
|
||||||
|
Snow,
|
||||||
|
Clear,
|
||||||
|
Cloudy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Icons {
|
||||||
|
|
||||||
|
pub fn get_icon_str(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Icons::Fahrenheight => Self::get_icon("e341").unwrap().to_string(),
|
||||||
|
Icons::Clock => Self::get_icon("e641").unwrap().to_string(),
|
||||||
|
Icons::Baseball => Self::get_icon("f0852").unwrap().to_string(),
|
||||||
|
Icons::Sunny => Self::get_icon("f0599").unwrap().to_string(),
|
||||||
|
Icons::Mixed => Self::get_icon("f067f").unwrap().to_string(),
|
||||||
|
Icons::Rain => Self::get_icon("e239").unwrap().to_string(),
|
||||||
|
Icons::Snow => Self::get_icon("f0f36").unwrap().to_string(),
|
||||||
|
Icons::Clear => Self::get_icon("e30d").unwrap().to_string(),
|
||||||
|
Icons::Cloudy => Self::get_icon("e312").unwrap().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_icon(code: &str) -> Option<char> {
|
||||||
|
u32::from_str_radix(&code, 16).ok().and_then(char::from_u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
28
src/main.rs
28
src/main.rs
@ -1,6 +1,7 @@
|
|||||||
mod weather;
|
mod weather;
|
||||||
mod redsox;
|
mod redsox;
|
||||||
mod nerdfont;
|
mod icons;
|
||||||
|
use icons::Icons;
|
||||||
use tabled::{Table, Tabled};
|
use tabled::{Table, Tabled};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tabled::settings::{
|
use tabled::settings::{
|
||||||
@ -18,22 +19,17 @@ struct TableRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Get forecast.
|
// Get forecast & schedule.
|
||||||
let entire_forecast: Vec<weather::WeatherPeriod> = weather::WeatherOfficeLocation {
|
let entire_forecast: Vec<weather::WeatherPeriod> = weather::WeatherOfficeLocation {
|
||||||
x: 75,
|
x: 75,
|
||||||
y: 59,
|
y: 59,
|
||||||
code: "GYX".to_string(),
|
code: "GYX".to_string(),
|
||||||
}.get_full_forecast();
|
}.get_full_forecast();
|
||||||
// Get sox schedule.
|
|
||||||
let sox_games: Vec<redsox::GameInfo> = redsox::get_schedule();
|
let sox_games: Vec<redsox::GameInfo> = redsox::get_schedule();
|
||||||
// Build icons.
|
// Build icons.
|
||||||
let baseball_icon = nerdfont::NerdFontIcon {
|
let baseball_icon = Icons::Baseball.get_icon_str();
|
||||||
icon_code: "f0852".to_string(),
|
let clock_icon = Icons::Clock.get_icon_str();
|
||||||
}.get_icon().unwrap();
|
let fahrenheight_icon = Icons::Fahrenheight.get_icon_str();
|
||||||
let clock_icon = nerdfont::NerdFontIcon {
|
|
||||||
icon_code: "e641".to_string(),
|
|
||||||
}.get_icon().unwrap();
|
|
||||||
|
|
||||||
// Build the rows for the table.
|
// Build the rows for the table.
|
||||||
let mut table_rows: Vec<TableRow> = vec![];
|
let mut table_rows: Vec<TableRow> = vec![];
|
||||||
for i in 0..entire_forecast.len() {
|
for i in 0..entire_forecast.len() {
|
||||||
@ -43,20 +39,10 @@ fn main() {
|
|||||||
// Check if there is a sox game and print opp.
|
// Check if there is a sox game and print opp.
|
||||||
for sox_game in &sox_games {
|
for sox_game in &sox_games {
|
||||||
if sox_game.date == yyyy_mm_dd {
|
if sox_game.date == yyyy_mm_dd {
|
||||||
sox_status = format!(
|
sox_status = format!("{} {}\n{} {}", &baseball_icon, &sox_game.opponent, &clock_icon, &sox_game.start_time);
|
||||||
"{} {}\n{} {}",
|
|
||||||
&baseball_icon,
|
|
||||||
&sox_game.opponent,
|
|
||||||
&clock_icon,
|
|
||||||
&sox_game.start_time,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Get fahrenheight icon;
|
|
||||||
let fahrenheight_icon = nerdfont::NerdFontIcon {
|
|
||||||
icon_code: "e341".to_string(),
|
|
||||||
}.get_icon().unwrap();
|
|
||||||
let row = TableRow {
|
let row = TableRow {
|
||||||
date: yyyy_mm_dd.to_string(),
|
date: yyyy_mm_dd.to_string(),
|
||||||
time_of_day: forecast_period.name.clone(),
|
time_of_day: forecast_period.name.clone(),
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -27,8 +27,8 @@ pub struct Game {
|
|||||||
pub game_date: String,
|
pub game_date: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
#[serde_alias(CamelCase,SnakeCase)]
|
#[serde_alias(CamelCase,SnakeCase)]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Teams {
|
pub struct Teams {
|
||||||
away: Team,
|
away: Team,
|
||||||
home: Team,
|
home: Team,
|
||||||
@ -55,6 +55,8 @@ pub struct TeamRecord {
|
|||||||
losses: i32,
|
losses: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[serde_alias(CamelCase,SnakeCase)]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct GameInfo {
|
pub struct GameInfo {
|
||||||
pub opponent: String,
|
pub opponent: String,
|
||||||
pub date: String,
|
pub date: String,
|
||||||
@ -85,11 +87,9 @@ pub fn get_schedule() -> Vec<GameInfo> {
|
|||||||
|
|
||||||
// Determine who the opponent is from the teams.
|
// Determine who the opponent is from the teams.
|
||||||
pub fn extract_opponent(teams: &Teams) -> String {
|
pub fn extract_opponent(teams: &Teams) -> String {
|
||||||
if teams.home.team.name == "Boston Red Sox" {
|
match teams.home.team.name == "Boston Red Sox" {
|
||||||
teams.away.team.name.to_string()
|
true => teams.away.team.name.to_string(),
|
||||||
}
|
false => teams.home.team.name.to_string(),
|
||||||
else {
|
|
||||||
teams.home.team.name.to_string()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,17 +100,42 @@ fn build_api_url() -> String {
|
|||||||
format!("{}{}{}", url_first, TEAM_ID, url_second)
|
format!("{}{}{}", url_first, TEAM_ID, url_second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the start time of the game.
|
||||||
fn get_start_time(iso_string: &String) -> String {
|
fn get_start_time(iso_string: &String) -> String {
|
||||||
let utc_dt: DateTime<Utc> = iso_string.parse().expect("Invalid ISO8601 string");
|
let utc_dt: DateTime<Utc> = iso_string.parse().expect("Invalid ISO8601 string");
|
||||||
let est_dt = utc_dt.with_timezone(&Eastern);
|
let est_dt = utc_dt.with_timezone(&Eastern);
|
||||||
est_dt.format("%I:%M").to_string()
|
est_dt.format("%I:%M").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod team_tests {
|
mod team_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn check_schedule_retrieval() {
|
fn test_build_api_url() {
|
||||||
get_schedule();
|
const EXPECTED_TEAM_ID: &str = "111";
|
||||||
|
const TEAM_ID: &str = EXPECTED_TEAM_ID;
|
||||||
|
|
||||||
|
fn build_api_url() -> String {
|
||||||
|
let url_first = "https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId=".to_string();
|
||||||
|
let url_second = "&startDate=2025-04-01&endDate=2025-09-30".to_string();
|
||||||
|
format!("{}{}{}", url_first, TEAM_ID, url_second)
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_url = format!(
|
||||||
|
"https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId={}&startDate=2025-04-01&endDate=2025-09-30",
|
||||||
|
TEAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(build_api_url(), expected_url);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_get_start_time() {
|
||||||
|
let iso_string = "2025-04-02T22:35:00Z".to_string(); // UTC time
|
||||||
|
let result = get_start_time(&iso_string);
|
||||||
|
|
||||||
|
// EST is UTC-5 or UTC-4 depending on DST. April is typically daylight saving (EDT = UTC-4)
|
||||||
|
assert_eq!(result, "06:35"); // 22:35 UTC == 18:35 EDT == 06:35 PM in 12-hour format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use reqwest::header::USER_AGENT;
|
use reqwest::header::USER_AGENT;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_alias::serde_alias;
|
use serde_alias::serde_alias;
|
||||||
#[path = "nerdfont.rs"] mod nerdfont;
|
#[path = "icons.rs"] mod icons;
|
||||||
|
use icons::Icons;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct ForecastWrapper {
|
struct ForecastWrapper {
|
||||||
@ -52,7 +53,11 @@ impl WeatherOfficeLocation {
|
|||||||
.send()
|
.send()
|
||||||
.expect("Unable to get data")
|
.expect("Unable to get data")
|
||||||
.text().unwrap().to_string();
|
.text().unwrap().to_string();
|
||||||
let ForecastWrapper { properties: Properties { mut periods } } = serde_json::from_str(&forecast).expect("JSON was not well-formatted");
|
let ForecastWrapper {
|
||||||
|
properties: Properties {
|
||||||
|
mut periods
|
||||||
|
}
|
||||||
|
} = serde_json::from_str(&forecast).expect("JSON was not well-formatted");
|
||||||
for period in periods.iter_mut() {
|
for period in periods.iter_mut() {
|
||||||
match detect_icon(&period.short_forecast) {
|
match detect_icon(&period.short_forecast) {
|
||||||
None => println!("There was an issue detecting the correct icon!!!"),
|
None => println!("There was an issue detecting the correct icon!!!"),
|
||||||
@ -66,45 +71,16 @@ impl WeatherOfficeLocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detect which icon to display based on short forecast.
|
// Detect which icon to display based on short forecast.
|
||||||
pub fn detect_icon(short_forecast: &String) -> Option<char> {
|
pub fn detect_icon(short_forecast: &str) -> Option<String> {
|
||||||
if short_forecast.contains("Sunny") {
|
match true {
|
||||||
let icon = nerdfont::NerdFontIcon {
|
_ if short_forecast.contains("Sunny") => Some(Icons::Sunny.get_icon_str()),
|
||||||
icon_code: "f0599".to_string(),
|
_ if short_forecast.contains("Rain") && short_forecast.contains("Snow") => {
|
||||||
};
|
Some(Icons::Mixed.get_icon_str())
|
||||||
let icon_code = icon.get_icon();
|
}
|
||||||
icon_code
|
_ if short_forecast.contains("Snow") => Some(Icons::Snow.get_icon_str()),
|
||||||
} else if short_forecast.contains("Rain") && short_forecast.contains("Snow") {
|
_ if short_forecast.contains("Rain") => Some(Icons::Rain.get_icon_str()),
|
||||||
let icon = nerdfont::NerdFontIcon {
|
_ if short_forecast.contains("Cloudy") => Some(Icons::Cloudy.get_icon_str()),
|
||||||
icon_code: "f067f".to_string(),
|
_ if short_forecast.contains("Clear") => Some(Icons::Clear.get_icon_str()),
|
||||||
};
|
_ => None,
|
||||||
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 if short_forecast.contains("Clear") {
|
|
||||||
let icon = nerdfont::NerdFontIcon {
|
|
||||||
icon_code: "e30d".to_string(),
|
|
||||||
};
|
|
||||||
let icon_code = icon.get_icon();
|
|
||||||
icon_code
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user