commit 40cb58189a29d8b347ced25f731c5de194405bf8
parent f61649b9fb73b5e7718ec70b146d07b8608bb99d
Author: mtmn <miro@haravara.org>
Date: Fri, 19 Jun 2026 13:52:11 +0200
feat: add --all flag and set it to default
Diffstat:
6 files changed, 89 insertions(+), 36 deletions(-)
diff --git a/README.md b/README.md
@@ -30,8 +30,8 @@ cargo build --release
* `-s`, `--standings`: Show league standings/table.
* `-t`, `--token <TOKEN>`: API token from football-data.org (required).
* `-f`, `--filter <TEAM>`: Filter by team name or abbreviation.
-* `--all`: Show all matches (past and future).
-* `-l`, `--league <LEAGUE>`: League to display (default: epl).
+* `-a`, `--all`: List all matches across every competition (ignores `--league`). This is the default when no `--league` is given.
+* `-l`, `--league <LEAGUE>`: League to display. When omitted, all competitions are shown.
### Leagues
@@ -43,17 +43,20 @@ cargo build --release
### Example
```sh
-# Show upcoming Premier League matches
+# List all matches across every competition (default)
./target/release/scores -t <TOKEN>
+# Show upcoming Premier League matches
+./target/release/scores -l epl -t <TOKEN>
+
# Show Premier League table
./target/release/scores -s -t <TOKEN>
-# Show upcoming Arsenal matches
+# List all matches involving Arsenal across every competition
./target/release/scores -f arsenal -t <TOKEN>
-# Show all Arsenal matches this season
-./target/release/scores -l epl -f ars -a -t <TOKEN>
+# Show upcoming Arsenal matches in the Premier League
+./target/release/scores -l epl -f arsenal -t <TOKEN>
# Show La Liga standings
./target/release/scores -s -l laliga -t <TOKEN>
diff --git a/src/api.rs b/src/api.rs
@@ -15,8 +15,8 @@ impl ApiClient {
}
}
- async fn fetch<T: for<'de> serde::Deserialize<'de>>(&self, endpoint: &str) -> Result<T> {
- let url = format!("https://api.football-data.org/v4/competitions/{endpoint}");
+ async fn fetch<T: for<'de> serde::Deserialize<'de>>(&self, path: &str) -> Result<T> {
+ let url = format!("https://api.football-data.org/v4/{path}");
let response = self
.client
@@ -33,10 +33,17 @@ impl ApiClient {
}
pub async fn fetch_matches(&self, league: League) -> Result<MatchesResponse> {
- self.fetch(&format!("{}/matches", league.code())).await
+ self.fetch(&format!("competitions/{}/matches", league.code()))
+ .await
}
pub async fn fetch_standings(&self, league: League) -> Result<StandingsResponse> {
- self.fetch(&format!("{}/standings", league.code())).await
+ self.fetch(&format!("competitions/{}/standings", league.code()))
+ .await
+ }
+
+ /// Fetch all matches across every available competition (`GET /v4/matches`).
+ pub async fn fetch_all_matches(&self) -> Result<MatchesResponse> {
+ self.fetch("matches").await
}
}
diff --git a/src/config.rs b/src/config.rs
@@ -17,7 +17,7 @@ pub struct Cli {
#[arg(long, short = 's')]
pub standings: bool,
- /// Show all matches (past and future)
+ /// List all matches across every competition (default; ignores --league)
#[arg(long, short = 'a')]
pub all: bool,
@@ -25,9 +25,9 @@ pub struct Cli {
#[arg(long, short = 'f')]
pub filter: Option<String>,
- /// League to display
- #[arg(long, short = 'l', default_value = "epl", value_name = "LEAGUE")]
- pub league: String,
+ /// League to display (defaults to all competitions when omitted)
+ #[arg(long, short = 'l', value_name = "LEAGUE")]
+ pub league: Option<String>,
}
impl Cli {
@@ -50,7 +50,7 @@ impl Cli {
pub struct Config {
pub api_token: String,
- pub league: League,
+ pub league: Option<League>,
pub show_all: bool,
pub show_standings: bool,
pub team_filter: Option<String>,
@@ -62,8 +62,12 @@ impl Config {
return Err(AppError::InvalidToken);
}
- let league = League::from_arg(&cli.league)
- .ok_or_else(|| AppError::InvalidLeague(cli.league.clone()))?;
+ let league = match &cli.league {
+ Some(arg) => {
+ Some(League::from_arg(arg).ok_or_else(|| AppError::InvalidLeague(arg.clone()))?)
+ }
+ None => None,
+ };
Ok(Self {
api_token: cli.token.clone(),
@@ -86,25 +90,39 @@ mod tests {
standings: false,
all: true,
filter: Some("arsenal".to_string()),
- league: "epl".to_string(),
+ league: Some("epl".to_string()),
};
let config = Config::from_cli(&cli).unwrap();
assert_eq!(config.api_token, "test-token");
- assert_eq!(config.league, League::PremierLeague);
+ assert_eq!(config.league, Some(League::PremierLeague));
assert!(config.show_all);
assert!(!config.show_standings);
assert_eq!(config.team_filter, Some("arsenal".to_string()));
}
#[test]
+ fn test_config_no_league_defaults_to_all() {
+ let cli = Cli {
+ token: "test-token".to_string(),
+ standings: false,
+ all: false,
+ filter: None,
+ league: None,
+ };
+
+ let config = Config::from_cli(&cli).unwrap();
+ assert_eq!(config.league, None);
+ }
+
+ #[test]
fn test_config_missing_token() {
let cli = Cli {
token: String::new(),
standings: false,
all: false,
filter: None,
- league: "epl".to_string(),
+ league: Some("epl".to_string()),
};
let result = Config::from_cli(&cli);
@@ -118,7 +136,7 @@ mod tests {
standings: false,
all: false,
filter: None,
- league: "bundesliga".to_string(),
+ league: Some("bundesliga".to_string()),
};
let result = Config::from_cli(&cli);
diff --git a/src/display.rs b/src/display.rs
@@ -59,7 +59,7 @@ pub fn filter_matches(
.collect()
}
-pub fn display_table(matches: &[Match]) {
+pub fn display_table(matches: &[Match], show_competition: bool) {
if matches.is_empty() {
println!("No matches found.");
return;
@@ -68,14 +68,20 @@ pub fn display_table(matches: &[Match]) {
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_BOX_CHARS);
- table.add_row(Row::new(vec![
+ let mut header = vec![
Cell::new("MD").style_spec("Fb"),
Cell::new("Date & Time").style_spec("Fb"),
+ ];
+ if show_competition {
+ header.push(Cell::new("Competition").style_spec("Fb"));
+ }
+ header.extend([
Cell::new("Home Team").style_spec("Fb"),
Cell::new("Score").style_spec("Fb"),
Cell::new("Away Team").style_spec("Fb"),
Cell::new("Status").style_spec("Fb"),
- ]));
+ ]);
+ table.add_row(Row::new(header));
for m in matches {
let matchday = m
@@ -101,14 +107,22 @@ pub fn display_table(matches: &[Match]) {
let home_team = m.home_team.name.as_deref().unwrap_or("Unknown");
let away_team = m.away_team.name.as_deref().unwrap_or("Unknown");
- table.add_row(Row::new(vec![
- Cell::new(&matchday),
- Cell::new(&date),
+ let mut cells = vec![Cell::new(&matchday), Cell::new(&date)];
+ if show_competition {
+ let competition = m
+ .competition
+ .as_ref()
+ .and_then(|c| c.name.as_deref())
+ .unwrap_or("-");
+ cells.push(Cell::new(competition));
+ }
+ cells.extend([
Cell::new(home_team).style_spec("r"),
Cell::new(&score).style_spec("Fyc"),
Cell::new(away_team).style_spec("l"),
Cell::new(&format!("{status_icon} {status}")),
- ]));
+ ]);
+ table.add_row(Row::new(cells));
}
table.printstd();
@@ -185,6 +199,7 @@ mod tests {
score: Score {
full_time: ScoreDetail::default(),
},
+ competition: None,
}
}
diff --git a/src/main.rs b/src/main.rs
@@ -7,6 +7,7 @@ mod models;
use config::Cli;
use error::Result;
+use league::League;
#[tokio::main]
async fn main() -> Result<()> {
@@ -15,16 +16,19 @@ async fn main() -> Result<()> {
let api_client = api::ApiClient::new(config.api_token);
if config.show_standings {
- let standings = api_client.fetch_standings(config.league).await?;
+ let league = config.league.unwrap_or(League::PremierLeague);
+ let standings = api_client.fetch_standings(league).await?;
display::display_standings(standings);
+ } else if let (false, Some(league)) = (config.show_all, config.league) {
+ let response = api_client.fetch_matches(league).await?;
+ let filtered =
+ display::filter_matches(response.matches, config.team_filter.as_deref(), false);
+ display::display_table(&filtered, false);
} else {
- let response = api_client.fetch_matches(config.league).await?;
- let filtered = display::filter_matches(
- response.matches,
- config.team_filter.as_deref(),
- config.show_all,
- );
- display::display_table(&filtered);
+ let response = api_client.fetch_all_matches().await?;
+ let filtered =
+ display::filter_matches(response.matches, config.team_filter.as_deref(), true);
+ display::display_table(&filtered, true);
}
Ok(())
diff --git a/src/models.rs b/src/models.rs
@@ -10,6 +10,12 @@ pub struct Match {
pub home_team: Team,
pub away_team: Team,
pub score: Score,
+ pub competition: Option<Competition>,
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Competition {
+ pub name: Option<String>,
}
#[derive(Debug, Deserialize, Clone)]