style(core): cargo fmt match/club agent additions
CI / Build, lint & test (push) Successful in 4m1s

This commit is contained in:
funman300
2026-08-20 17:59:02 +00:00
parent 5f9f556af8
commit 2fb835200f
4 changed files with 108 additions and 83 deletions
+4 -3
View File
@@ -5,10 +5,10 @@ use crate::{
services::{club as club_svc, notification}, services::{club as club_svc, notification},
}; };
use anyhow::Context; use anyhow::Context;
use std::path::Path;
use uuid::Uuid;
use sqlx::{Sqlite, Transaction}; use sqlx::{Sqlite, Transaction};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::Path;
use uuid::Uuid;
pub fn load_achievement_definitions(data_dir: &str) -> anyhow::Result<Vec<AchievementDefinition>> { pub fn load_achievement_definitions(data_dir: &str) -> anyhow::Result<Vec<AchievementDefinition>> {
let dir = Path::new(data_dir).join("achievements"); let dir = Path::new(data_dir).join("achievements");
@@ -196,7 +196,8 @@ async fn metric_value_tx(
club_id: &str, club_id: &str,
trigger: &str, trigger: &str,
) -> AppResult<i64> { ) -> AppResult<i64> {
let v: i64 = match trigger { let v: i64 =
match trigger {
"matches_played" => { "matches_played" => {
sqlx::query_scalar("SELECT matches_played FROM statistics WHERE profile_id = ?") sqlx::query_scalar("SELECT matches_played FROM statistics WHERE profile_id = ?")
.bind(profile_id) .bind(profile_id)
+6 -10
View File
@@ -140,14 +140,12 @@ const OWNED_SELECT: &str = "SELECT id, club_id, card_id, is_loan, loan_matches_r
/// The club's most-recently-updated squad id (its "active" squad), matching the /// The club's most-recently-updated squad id (its "active" squad), matching the
/// selection `squad::get_squad` uses, or `None` when the club has no squad yet. /// selection `squad::get_squad` uses, or `None` when the club has no squad yet.
pub async fn active_squad_id(pool: &Pool, club_id: &str) -> AppResult<Option<String>> { pub async fn active_squad_id(pool: &Pool, club_id: &str) -> AppResult<Option<String>> {
Ok( Ok(sqlx::query_scalar::<_, String>(
sqlx::query_scalar::<_, String>(
"SELECT id FROM squads WHERE club_id = ? ORDER BY updated_at DESC LIMIT 1", "SELECT id FROM squads WHERE club_id = ? ORDER BY updated_at DESC LIMIT 1",
) )
.bind(club_id) .bind(club_id)
.fetch_optional(pool) .fetch_optional(pool)
.await?, .await?)
)
} }
/// The owned card assigned as the manager of `club_id`'s active squad, if any. /// The owned card assigned as the manager of `club_id`'s active squad, if any.
@@ -182,11 +180,7 @@ pub async fn get_squad_manager_for_squad(
/// belong to `club_id`, so a client can neither manage another club's squad nor /// belong to `club_id`, so a client can neither manage another club's squad nor
/// assign a card it does not own. One manager per squad (the PK REPLACE), so a /// assign a card it does not own. One manager per squad (the PK REPLACE), so a
/// re-assignment never accumulates duplicate rows. /// re-assignment never accumulates duplicate rows.
pub async fn set_squad_manager( pub async fn set_squad_manager(pool: &Pool, club_id: &str, owned_card_id: &str) -> AppResult<()> {
pool: &Pool,
club_id: &str,
owned_card_id: &str,
) -> AppResult<()> {
let squad_id = active_squad_id(pool, club_id) let squad_id = active_squad_id(pool, club_id)
.await? .await?
.ok_or_else(|| AppError::NotFound("club has no squad to assign a manager to".into()))?; .ok_or_else(|| AppError::NotFound("club has no squad to assign a manager to".into()))?;
@@ -304,7 +298,9 @@ mod tests {
let (dir, url, pool) = fixture().await; let (dir, url, pool) = fixture().await;
// SAVE. // SAVE.
set_squad_manager(&pool, "club-a", "mgr").await.expect("assign"); set_squad_manager(&pool, "club-a", "mgr")
.await
.expect("assign");
// RELOAD (same pool). // RELOAD (same pool).
let got = get_squad_manager(&pool, "club-a").await.unwrap(); let got = get_squad_manager(&pool, "club-a").await.unwrap();
assert_eq!(got.as_ref().map(|c| c.id.as_str()), Some("mgr")); assert_eq!(got.as_ref().map(|c| c.id.as_str()), Some("mgr"));
+34 -6
View File
@@ -438,7 +438,9 @@ async fn complete_match_inner(
)); ));
} }
if req.match_identity.trim().is_empty() { if req.match_identity.trim().is_empty() {
return Err(AppError::BadRequest("match_identity must not be empty".into())); return Err(AppError::BadRequest(
"match_identity must not be empty".into(),
));
} }
let result = req.result; let result = req.result;
@@ -905,7 +907,11 @@ mod match_completion_tests {
assert_eq!(r.xp_awarded, XP_LOSS); assert_eq!(r.xp_awarded, XP_LOSS);
assert_eq!(r.match_record.outcome, "dnf"); assert_eq!(r.match_record.outcome, "dnf");
assert_eq!(stat(&fx.pool, "matches_dnf").await, 1); assert_eq!(stat(&fx.pool, "matches_dnf").await, 1);
assert_eq!(stat(&fx.pool, "matches_lost").await, 0, "DNF is not a loss row"); assert_eq!(
stat(&fx.pool, "matches_lost").await,
0,
"DNF is not a loss row"
);
assert_eq!(stat(&fx.pool, "matches_played").await, 1); assert_eq!(stat(&fx.pool, "matches_played").await, 1);
assert_eq!(coins(&fx.pool).await, START_COINS + COINS_LOSS); assert_eq!(coins(&fx.pool).await, START_COINS + COINS_LOSS);
} }
@@ -922,7 +928,11 @@ mod match_completion_tests {
assert_eq!(r.match_record.outcome, "no_contest"); assert_eq!(r.match_record.outcome, "no_contest");
assert_eq!(coins(&fx.pool).await, START_COINS, "no coins for a void"); assert_eq!(coins(&fx.pool).await, START_COINS, "no coins for a void");
assert_eq!(xp(&fx.pool).await, 0); assert_eq!(xp(&fx.pool).await, 0);
assert_eq!(stat(&fx.pool, "matches_played").await, 0, "not counted as played"); assert_eq!(
stat(&fx.pool, "matches_played").await,
0,
"not counted as played"
);
// Still recorded for history + idempotency. // Still recorded for history + idempotency.
assert_eq!(count(&fx.pool, "matches").await, 1); assert_eq!(count(&fx.pool, "matches").await, 1);
assert_eq!(count(&fx.pool, "match_completions").await, 1); assert_eq!(count(&fx.pool, "match_completions").await, 1);
@@ -1089,7 +1099,11 @@ mod match_completion_tests {
.await .await
.unwrap(); .unwrap();
assert!(retry.applied, "{fp:?} retry-after-fault must apply"); assert!(retry.applied, "{fp:?} retry-after-fault must apply");
assert_eq!(coins(&fx.pool).await, START_COINS + COINS_WIN, "{fp:?} retry"); assert_eq!(
coins(&fx.pool).await,
START_COINS + COINS_WIN,
"{fp:?} retry"
);
} }
} }
@@ -1129,7 +1143,14 @@ mod match_completion_tests {
]; ];
let achs = vec![ach("first_win", "matches_won", 1, 50)]; let achs = vec![ach("first_win", "matches_won", 1, 50)];
let first = complete_match(&fx.pool, PROFILE, CLUB, &req("m", MatchResultKind::Win, 2, 0), &objs, &achs) let first = complete_match(
&fx.pool,
PROFILE,
CLUB,
&req("m", MatchResultKind::Win, 2, 0),
&objs,
&achs,
)
.await .await
.unwrap(); .unwrap();
assert!(first.applied); assert!(first.applied);
@@ -1140,7 +1161,14 @@ mod match_completion_tests {
assert_eq!(count(&fx.pool, "player_achievements").await, 1); assert_eq!(count(&fx.pool, "player_achievements").await, 1);
// Replay: no double objectives/achievements/coins. // Replay: no double objectives/achievements/coins.
let replay = complete_match(&fx.pool, PROFILE, CLUB, &req("m", MatchResultKind::Win, 2, 0), &objs, &achs) let replay = complete_match(
&fx.pool,
PROFILE,
CLUB,
&req("m", MatchResultKind::Win, 2, 0),
&objs,
&achs,
)
.await .await
.unwrap(); .unwrap();
assert!(!replay.applied); assert!(!replay.applied);
+1 -1
View File
@@ -6,9 +6,9 @@ use crate::{
}, },
}; };
use anyhow::Context; use anyhow::Context;
use sqlx::{Sqlite, Transaction};
use std::path::Path; use std::path::Path;
use uuid::Uuid; use uuid::Uuid;
use sqlx::{Sqlite, Transaction};
pub fn load_objective_definitions(data_dir: &str) -> anyhow::Result<Vec<ObjectiveDefinition>> { pub fn load_objective_definitions(data_dir: &str) -> anyhow::Result<Vec<ObjectiveDefinition>> {
let dir = Path::new(data_dir).join("objectives"); let dir = Path::new(data_dir).join("objectives");