Skip to content

Commit

Permalink
feat: refactored drivers standings code (I think it's pretty finished…
Browse files Browse the repository at this point in the history
… for this) TODO: parameters validation
  • Loading branch information
thibault-cne committed Jan 30, 2024
1 parent 7ff8395 commit 414992a
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 160 deletions.
86 changes: 52 additions & 34 deletions api/src/standings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ pub fn standing(
series: Series,
param: DriverStandingParameter,
) -> Result<Json<StandingsResponse>> {
let (drivers_standings, pagination) = driver_standing_inner_handler(db, series, None, param)?;

let response = StandingsResponse {
pagination,
series,
drivers_standings,
constructors_standings: Vec::new(),
};
let response = driver_standing_inner_handler(db, series, None, None, param)?;

Ok(Json(response))
}
Expand All @@ -35,15 +28,20 @@ pub fn standing_with_year(
param: DriverStandingParameter,
year: Year,
) -> Result<Json<StandingsResponse>> {
let (drivers_standings, pagination) =
driver_standing_inner_handler(db, series, Some(year), param)?;
let response = driver_standing_inner_handler(db, series, Some(year), None, param)?;

let response = StandingsResponse {
pagination,
series,
drivers_standings,
constructors_standings: Vec::new(),
};
Ok(Json(response))
}

#[get("/<series>/<year>/<round>/standings/drivers?<param..>", rank = 1)]
pub fn standing_with_year_and_round(
db: &State<ConnectionPool>,
series: Series,
param: DriverStandingParameter,
year: Year,
round: Round,
) -> Result<Json<StandingsResponse>> {
let response = driver_standing_inner_handler(db, series, Some(year), Some(round), param)?;

Ok(Json(response))
}
Expand All @@ -52,34 +50,54 @@ fn driver_standing_inner_handler(
db: &State<ConnectionPool>,
series: Series,
year: Option<Year>,
round: Option<Round>,
param: DriverStandingParameter,
) -> Result<(Vec<DriverStanding>, Pagination)> {
) -> Result<StandingsResponse> {
let f = |conn: &mut PooledConnection<ConnectionManager<MysqlConnection>>| {
let filter: DriverStandingFilter = match year {
Some(year) => {
let race = models::Race::last_race_of_year(year).first(conn)?;
let mut filter: DriverStandingFilter = param.into();
filter.race_id = Some(race.race_id.into());
filter
}
None => param.into(),
let filter: DriverStandingFilter = if let Some(year) = year {
let race = if let Some(round) = round {
models::Race::by_year_and_round(year, round).first(conn)?
} else {
models::Race::last_race_of_year(year).first(conn)?
};
let mut filter: DriverStandingFilter = param.into();
filter.race_id = Some(race.race_id.into());
filter
} else {
param.into()
};

let (vec, pagination) =
application::models::DriverStanding::filter(filter).load_and_count_pages(conn)?;
application::builders::DriverStandingBuilder::new(filter).load(conn)?;

Ok((
vec.into_iter()
.map(DriverStanding::from)
.collect::<Vec<_>>(),
pagination,
))
Ok::<_, crate::error::Error>((vec, pagination))
};

let pool = &mut db.from_series(series).get()?;
pool.transaction(f)
let (drivers_standings, pagination) = pool.transaction(f)?;
let (season, round) = if let Some(f) = drivers_standings.first() {
(
Some(f.race_round_and_year.year),
Some(f.race_round_and_year.round),
)
} else {
(None, None)
};
let drivers_standings = drivers_standings
.into_iter()
.map(|s| s.into())
.collect::<Vec<_>>();

Ok(StandingsResponse {
season,
round,
drivers_standings,
constructors_standings: Vec::new(),
pagination,
series,
})
}

pub fn handlers() -> Vec<rocket::Route> {
routes![standing]
routes![standing, standing_with_year, standing_with_year_and_round]
}
216 changes: 112 additions & 104 deletions application/src/driver_standing.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,45 @@
use diesel::{
alias,
helper_types::{
And, AsSelect, Eq, InnerJoin, InnerJoinOn, InnerJoinQuerySource, IntoBoxed, LeftJoinOn,
LeftJoinQuerySource, Lt, Select,
},
prelude::*,
query_source::{Alias, AliasedField},
sql_types::{Bool, Nullable},
};
use shared::filters::DriverStandingFilter;
use diesel::{alias, prelude::*};
use shared::models::Pagination;

use crate::models::DriverStanding;
use crate::prelude::*;

type BoxedConditionSource = InnerJoinQuerySource<
InnerJoinQuerySource<
InnerJoinQuerySource<
InnerJoinQuerySource<
LeftJoinQuerySource<
races::table,
Alias<RaceAlias>,
And<
Eq<races::year, AliasedField<RaceAlias, races::year>>,
Lt<races::round, AliasedField<RaceAlias, races::round>>,
>,
>,
driverStandings::table,
>,
drivers::table,
Eq<drivers::driver_id, driverStandings::driver_id>,
>,
results::table,
And<Eq<drivers::driver_id, results::driver_id>, Eq<races::race_id, results::race_id>>,
>,
constructors::table,
Eq<results::constructor_id, constructors::constructor_id>,
>;
type BoxedCondition =
Box<dyn BoxableExpression<BoxedConditionSource, super::Backend, SqlType = Nullable<Bool>>>;

type BoxedQuerySource = InnerJoinOn<
InnerJoinOn<
InnerJoinOn<
InnerJoin<
LeftJoinOn<
races::table,
Alias<RaceAlias>,
And<
Eq<races::year, AliasedField<RaceAlias, races::year>>,
Lt<races::round, AliasedField<RaceAlias, races::round>>,
>,
>,
driverStandings::table,
>,
drivers::table,
Eq<drivers::driver_id, driverStandings::driver_id>,
>,
results::table,
And<Eq<drivers::driver_id, results::driver_id>, Eq<races::race_id, results::race_id>>,
>,
constructors::table,
Eq<results::constructor_id, constructors::constructor_id>,
>;
pub type BoxedQuery = IntoBoxed<
'static,
Select<BoxedQuerySource, AsSelect<DriverStanding, super::Backend>>,
super::Backend,
>;
use types::*;

alias!(races as r1: RaceAlias);

impl DriverStanding {
fn boxed() -> BoxedQuery {
pub struct DriverStandingBuilder(shared::filters::DriverStandingFilter);

impl DriverStandingBuilder {
pub fn new(filter: shared::filters::DriverStandingFilter) -> Self {
Self(filter)
}

pub fn load(
self,
conn: &mut MysqlConnection,
) -> Result<(Vec<DriverStanding>, Pagination), diesel::result::Error> {
let page = self.0.page.unwrap_or_default().0 as _;
let limit = self.0.limit.unwrap_or_default().0 as _;

if let Some(race_id) = self.0.race_id {
Self::by_race_id(race_id.0)
.paginate(page)
.per_page(limit)
.load_and_count_pages(conn)
} else if let Some(driver_ref) = self.0.name {
Self::by_driver_ref(driver_ref.0)
.paginate(page)
.per_page(limit)
.load_and_count_pages(conn)
} else {
Self::by_result(self.0.result.unwrap().0)
.paginate(page)
.per_page(limit)
.load_and_count_pages(conn)
}
}

fn all_left_join() -> All<DSLeftJoin> {
races::table
.left_join(
r1.on(races::year
Expand All @@ -80,52 +48,92 @@ impl DriverStanding {
)
.inner_join(driverStandings::table)
.inner_join(drivers::table.on(drivers::driver_id.eq(driverStandings::driver_id)))
.inner_join(
results::table.on(drivers::driver_id
.eq(results::driver_id)
.and(races::race_id.eq(results::race_id))),
)
.inner_join(
constructors::table.on(results::constructor_id.eq(constructors::constructor_id)),
)
.select(DriverStanding::as_select())
.into_boxed()
}

pub fn filter(filter: DriverStandingFilter) -> Paginated<BoxedQuery> {
let limit = filter.limit.unwrap_or_default().0 as i64;
let page = filter.page.unwrap_or_default().0 as i64;
fn default_join() -> All<DSDefaultJoin> {
driverStandings::table
.inner_join(races::table)
.inner_join(drivers::table.on(driverStandings::driver_id.eq(drivers::driver_id)))
.select(DriverStanding::as_select())
}

let mut conditions = fields_to_filter!(
filter,
name => (DriverRef, StringFilter::Equal),
result => (Result, NumberFilter::Equal)
);
conditions.push(Condition::RaceIdNull);
fn by_race_id(race_id: i32) -> By<All<DSDefaultJoin>, RaceId> {
Self::default_join().filter(races::race_id.eq(race_id))
}

let filter = match create_filter!(conditions, AndOr::And) {
Some(boxed_condition) => boxed_condition,
None => return Self::boxed().paginate(page).per_page(limit),
};
fn by_driver_ref(driver_ref: String) -> By<All<DSLeftJoin>, DriverRef> {
Self::all_left_join().filter(
drivers::driver_ref
.eq(driver_ref)
.and(r1.field(races::race_id).is_null()),
)
}

Self::boxed().filter(filter).paginate(page).per_page(limit)
fn by_result(result: i32) -> By<All<DSLeftJoin>, DSResult> {
Self::all_left_join().filter(
driverStandings::position
.eq(result)
.and(r1.field(races::race_id).is_null()),
)
}
}

enum Condition {
Result(NumberFilter<i32>),
DriverRef(StringFilter),
RaceIdNull,
}
mod types {
use diesel::{
helper_types::{And, AsSelect, Eq, Filter, InnerJoin, InnerJoinOn, LeftJoinOn, Lt, Select},
query_source::{Alias, AliasedField},
};

impl Condition {
fn into_boxed_condition(self) -> Option<BoxedCondition> {
use Condition::*;
use super::RaceAlias;
use crate::schema::*;

pub type All<T> = Select<T, AsSelect<super::DriverStanding, crate::Backend>>;
pub type By<S, F> = Filter<S, F>;
pub type DSLeftJoin = InnerJoinOn<
InnerJoin<
LeftJoinOn<
races::table,
Alias<RaceAlias>,
And<
Eq<races::year, AliasedField<RaceAlias, races::year>>,
Lt<races::round, AliasedField<RaceAlias, races::round>>,
>,
>,
driverStandings::table,
>,
drivers::table,
Eq<drivers::driver_id, driverStandings::driver_id>,
>;
pub type DSDefaultJoin = InnerJoinOn<
InnerJoin<driverStandings::table, races::table>,
drivers::table,
Eq<driverStandings::driver_id, drivers::driver_id>,
>;

macro_rules! operators {
($name:ident => $op:ident { $column:path, $type:ty }; $($rest:tt)*) => {
pub type $name = diesel::helper_types::$op < $column, $type >;
operators! { $($rest)* }
};
($name:ident => $op:ident { $type:ty }; $($rest:tt)*) => {
pub type $name = diesel::helper_types::$op < $type >;
operators! { $($rest)* }
};
($name:ident => alias @ { $alias:ident, $column:path }; $($rest:tt)*) => {
pub type $name = diesel::query_source::AliasedField < $alias, $column >;
operators! { $($rest)* }
};
() => {}
}

Some(match self {
DriverRef(f) => string_filter!(f, drivers::driver_ref),
Result(f) => number_filter!(f, driverStandings::position),
RaceIdNull => Box::new(r1.fields(races::race_id).is_null().nullable()),
})
operators! {
_R1IdIsNull => alias @ { RaceAlias, races::race_id };
R1IdIsNull => IsNull { _R1IdIsNull };
RaceId => Eq { races::race_id, i32 };
_DriverRef => Eq { drivers::driver_ref, String };
_DSResult => Eq { driverStandings::position, i32 };
DriverRef => And { _DriverRef, R1IdIsNull };
DSResult => And { _DSResult, R1IdIsNull };
}
}
4 changes: 4 additions & 0 deletions application/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ mod prelude {
pub use super::schema::*;
}

pub mod builders {
pub use super::driver_standing::DriverStandingBuilder;
}

pub mod filters {
#![allow(unused_imports, unused_macros)]

Expand Down
Loading

0 comments on commit 414992a

Please sign in to comment.