Better handling for icons, moving things to impl for weather.
This commit is contained in:
parent
d3f6691ca9
commit
5da8701978
42
src/main.rs
42
src/main.rs
@ -1,6 +1,7 @@
|
|||||||
#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
|
#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
|
||||||
mod weather;
|
mod weather;
|
||||||
mod redsox;
|
mod redsox;
|
||||||
|
mod nerdfont;
|
||||||
// use colored::Colorize;
|
// use colored::Colorize;
|
||||||
use tabled::{Table, Tabled};
|
use tabled::{Table, Tabled};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -13,22 +14,30 @@ use tabled::settings::{
|
|||||||
struct TableRow {
|
struct TableRow {
|
||||||
date: String,
|
date: String,
|
||||||
time_of_day: String,
|
time_of_day: String,
|
||||||
temp: i32,
|
temp: String,
|
||||||
red_sox: String,
|
red_sox: String,
|
||||||
forecast: String,
|
forecast: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
fn main() {
|
fn main() {
|
||||||
// Set the weather location here.
|
// Get forecast.
|
||||||
let location = 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();
|
||||||
let entire_forecast: Vec<weather::WeatherPeriod> = weather::get_full_forecast(location);
|
// Get sox schedule.
|
||||||
let sox_games: Vec<redsox::GameInfo> = redsox::get_schedule();
|
let sox_games: Vec<redsox::GameInfo> = 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<TableRow> = vec![];
|
let mut table_rows: Vec<TableRow> = vec![];
|
||||||
for i in 0..entire_forecast.len() {
|
for i in 0..entire_forecast.len() {
|
||||||
let forecast_period = &entire_forecast[i];
|
let forecast_period = &entire_forecast[i];
|
||||||
@ -37,17 +46,19 @@ 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 {
|
||||||
let mut opp_str = String::from(baseball_diamond);
|
// @todo - currently hardcoding time - figure out how to get it.
|
||||||
opp_str.push_str(" ");
|
sox_status = format!("{} {}\n{} {}", baseball_icon, &sox_game.opponent, clock_icon, "8:00".to_string());
|
||||||
opp_str.push_str(&sox_game.opponent);
|
|
||||||
sox_status = opp_str;
|
|
||||||
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(),
|
||||||
temp: forecast_period.temperature,
|
temp: format!("{}{}", forecast_period.temperature, fahrenheight_icon),
|
||||||
red_sox: sox_status,
|
red_sox: sox_status,
|
||||||
forecast: forecast_period.detailed_forecast.to_string(),
|
forecast: forecast_period.detailed_forecast.to_string(),
|
||||||
};
|
};
|
||||||
@ -59,12 +70,13 @@ fn main() {
|
|||||||
fn render_table(rows: &Vec<TableRow>) {
|
fn render_table(rows: &Vec<TableRow>) {
|
||||||
// here is where we actually render the table.
|
// here is where we actually render the table.
|
||||||
let mut table = Table::new(rows);
|
let mut table = Table::new(rows);
|
||||||
table.with(Style::modern());
|
table.with((Style::modern(), Alignment::center()));
|
||||||
|
table.modify(Columns::last(), Alignment::left());
|
||||||
table.with((
|
table.with((
|
||||||
Width::wrap(210).priority(Priority::max(true)),
|
Width::wrap(195).priority(Priority::max(true)),
|
||||||
Width::increase(50).priority(Priority::min(true)),
|
Width::increase(60).priority(Priority::min(true)),
|
||||||
));
|
));
|
||||||
table.modify(Columns::first(), Alignment::right());
|
table.modify(Columns::single(3), Alignment::left());
|
||||||
println!("{}", table);
|
println!("{}", table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_alias::serde_alias;
|
use serde_alias::serde_alias;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use iso8601;
|
// use iso8601;
|
||||||
|
|
||||||
const SOX_ID: i32 = 111;
|
const TEAM_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";
|
|
||||||
|
|
||||||
#[serde_alias(CamelCase,SnakeCase)]
|
#[serde_alias(CamelCase,SnakeCase)]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
@ -68,11 +66,9 @@ pub struct GameInfo {
|
|||||||
// Gets the full forecast from the response.
|
// Gets the full forecast from the response.
|
||||||
pub fn get_schedule() -> Vec<GameInfo> {
|
pub fn get_schedule() -> Vec<GameInfo> {
|
||||||
let client = reqwest::blocking::Client::new();
|
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_url = build_api_url();
|
||||||
let schedule_json: String = client.get(SOX_SCHEDULE_URL).send().expect("Unable to get data").text().unwrap().to_string();
|
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");
|
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<GameInfo> = vec![];
|
let mut full_schedule: Vec<GameInfo> = vec![];
|
||||||
let dates = schedule.dates;
|
let dates = schedule.dates;
|
||||||
for date in dates {
|
for date in dates {
|
||||||
@ -88,6 +84,7 @@ pub fn get_schedule() -> Vec<GameInfo> {
|
|||||||
full_schedule
|
full_schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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" {
|
if teams.home.team.name == "Boston Red Sox" {
|
||||||
teams.away.team.name.to_string()
|
teams.away.team.name.to_string()
|
||||||
@ -97,10 +94,16 @@ pub fn extract_opponent(teams: &Teams) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// Build the url for the api request.
|
||||||
mod sox_tests {
|
fn build_api_url() -> String {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn check_schedule_retrieval() {
|
fn check_schedule_retrieval() {
|
||||||
get_schedule();
|
get_schedule();
|
||||||
|
|||||||
@ -33,6 +33,7 @@ pub struct WeatherOfficeLocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WeatherOfficeLocation {
|
impl WeatherOfficeLocation {
|
||||||
|
// Build api request URL.
|
||||||
pub fn build_url(&self) -> String {
|
pub fn build_url(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"https://api.weather.gov/gridpoints/{}/{},{}/forecast",
|
"https://api.weather.gov/gridpoints/{}/{},{}/forecast",
|
||||||
@ -41,31 +42,30 @@ impl WeatherOfficeLocation {
|
|||||||
self.y,
|
self.y,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Get the full forecast for the location.
|
||||||
// Gets the full forecast from the response.
|
pub fn get_full_forecast(&self) -> Vec<WeatherPeriod> {
|
||||||
pub fn get_full_forecast(location: WeatherOfficeLocation) -> Vec<WeatherPeriod> {
|
let url = WeatherOfficeLocation::build_url(&self);
|
||||||
let url = WeatherOfficeLocation::build_url(&location);
|
let client = reqwest::blocking::Client::new();
|
||||||
let client = reqwest::blocking::Client::new();
|
let forecast = client.get(&url)
|
||||||
let forecast = client.get(&url)
|
.header(USER_AGENT, "My SuperAwesome Weather App")
|
||||||
.header(USER_AGENT, "My SuperAwesome Weather App")
|
.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() {
|
||||||
// let json: ForecastWrapper = serde_json::from_str(&forecast).expect("JSON was not well-formatted");
|
match detect_icon(&period.short_forecast) {
|
||||||
// let mut weather_periods: Vec<WeatherPeriod> = json.properties.periods.into_iter().collect();
|
None => println!("There was an issue detecting the correct icon!!!"),
|
||||||
for period in periods.iter_mut() {
|
Some(icon) => {
|
||||||
match detect_icon(&period.short_forecast) {
|
period.detailed_forecast = format!("{} {}", icon, &period.detailed_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<char> {
|
pub fn detect_icon(short_forecast: &String) -> Option<char> {
|
||||||
if short_forecast.contains("Sunny") {
|
if short_forecast.contains("Sunny") {
|
||||||
let icon = nerdfont::NerdFontIcon {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
@ -73,45 +73,38 @@ pub fn detect_icon(short_forecast: &String) -> Option<char> {
|
|||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
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 {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
icon_code: "f067f".to_string(),
|
icon_code: "f067f".to_string(),
|
||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
icon_code
|
||||||
}
|
} else if short_forecast.contains("Snow") {
|
||||||
else if short_forecast.contains("Snow") {
|
|
||||||
let icon = nerdfont::NerdFontIcon {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
icon_code: "f0f36".to_string(),
|
icon_code: "f0f36".to_string(),
|
||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
icon_code
|
||||||
}
|
} else if short_forecast.contains("Rain") {
|
||||||
else if short_forecast.contains("Rain") {
|
|
||||||
let icon = nerdfont::NerdFontIcon {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
icon_code: "e239".to_string(),
|
icon_code: "e239".to_string(),
|
||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
icon_code
|
||||||
}
|
} else if short_forecast.contains("Cloudy") {
|
||||||
else if short_forecast.contains("Cloudy") {
|
|
||||||
let icon = nerdfont::NerdFontIcon {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
icon_code: "e312".to_string(),
|
icon_code: "e312".to_string(),
|
||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
icon_code
|
||||||
}
|
} else if short_forecast.contains("Clear") {
|
||||||
else if short_forecast.contains("Clear") {
|
|
||||||
let icon = nerdfont::NerdFontIcon {
|
let icon = nerdfont::NerdFontIcon {
|
||||||
icon_code: "e30d".to_string(),
|
icon_code: "e30d".to_string(),
|
||||||
};
|
};
|
||||||
let icon_code = icon.get_icon();
|
let icon_code = icon.get_icon();
|
||||||
icon_code
|
icon_code
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user