Initial commit in new repo with new history.

This commit is contained in:
calcu1on
2025-04-02 20:39:30 -04:00
commit 8e1ae39164
7 changed files with 2731 additions and 0 deletions

73
src/main.rs Normal file
View File

@@ -0,0 +1,73 @@
#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
mod weather;
mod redsox;
use tabled::Table;
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")]
struct TableRow {
date: String,
time_of_day: String,
temp: u64,
red_sox: String,
forecast: String,
}
#[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,
y: 59,
code: "GYX".to_string(),
};
// @todo - add a way to configure which teams to add
// @todo - add a way to get news articles?
let entire_forecast: Vec<weather::WeatherPeriod> = weather::get_full_forecast(location);
let sox_games: Vec<redsox::Game> = redsox::get_upcoming_games();
let mut table_rows: Vec<TableRow> = vec![];
for i in 0..entire_forecast.len() {
let item = &entire_forecast[i];
let date = &item.start_time[0..10];
let mut sox_status = String::new();
// Check if there is a sox game and print opp.
for sox_game in &sox_games {
if sox_game.date == date {
let mut opp_str = String::new();
opp_str.push(baseball_diamond);
opp_str.push_str(" ");
opp_str.push_str(&sox_game.opponent);
sox_status = opp_str;
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,
};
table_rows.push(row);
}
let mut table = Table::new(table_rows);
table.with(Style::modern());
table.with((
Width::wrap(210).priority(Priority::max(true)),
Width::increase(50).priority(Priority::min(true)),
));
table.modify(Columns::first(), Alignment::right());
println!("{}", table);
}

72
src/redsox/mod.rs Normal file
View File

@@ -0,0 +1,72 @@
use serde::{Deserialize, Serialize};
use serde_alias::serde_alias;
use std::fs;
const SOX_ID: i32 = 111;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct Schedule {
pub games: Vec<Game>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct Game {
// pub teams: Teams,
pub date: String,
// pub start_time: String,
pub opponent: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct Teams {
away: Team,
home: Team,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct Team {
team: TeamInfo,
league_record: TeamRecord,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct TeamInfo {
id: i32,
name: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde_alias(CamelCase,SnakeCase)]
pub struct TeamRecord {
wins: i32,
losses: i32,
}
pub fn get_upcoming_games() -> Vec<Game> {
// @todo - change this to be a dynamic request from the API endpoint instead of a local file.
let schedule_json: String = fs::read_to_string("/Users/danchadwick/Projects/rust/weather/assets/sox-schedule.json").expect("Unable to read file").to_owned();
let json: Vec<Game> = serde_json::from_str(&schedule_json).expect("Something not good?");
let upcoming_games: &Vec<Game> = &json.into_iter().take(7).collect();
upcoming_games.to_owned()
}
// Gets the full forecast from the response.
pub fn get_schedule() -> Schedule {
let url = "https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId=111&startDate=2025-04-01&endDate=2025-09-30".to_string();
let client = reqwest::blocking::Client::new();
let schedule_string: String = client.get(&url).send().expect("Unable to get data").text().unwrap().to_string();
let json: Schedule = serde_json::from_str(&schedule_string).expect("JSON was not well-formatted");
dbg!(json);
let schedule = Schedule {
games: vec![],
};
schedule
// json
}

54
src/weather/mod.rs Normal file
View File

@@ -0,0 +1,54 @@
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
}