Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "chron
validator = { version = "0.20.0", features = ["derive"] }
regex = "1.0"

lazy_static = "1.4"
lazy_static = "1.4"
uuid = "1.18.1"
17 changes: 17 additions & 0 deletions src/models/beatmap/beatmap/impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use super::query::{find_by_id, find_by_osu_id, insert};
use super::BeatmapRow;
use sqlx::{Error as SqlxError, PgPool};

impl BeatmapRow {
pub async fn insert(self, pool: &PgPool) -> Result<i32, SqlxError> {
insert(pool, self).await
}

pub async fn find_by_id(pool: &PgPool, id: i32) -> Result<Self, SqlxError> {
find_by_id(pool, id).await?.ok_or(SqlxError::RowNotFound)
}

pub async fn find_by_osu_id(pool: &PgPool, osu_id: i32) -> Result<Option<Self>, SqlxError> {
find_by_osu_id(pool, osu_id).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ pub mod validators;
#[cfg(test)]
mod tests;

pub use types::*;
pub use types::BeatmapRow;
30 changes: 30 additions & 0 deletions src/models/beatmap/beatmap/query/by_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::models::beatmap::beatmap::types::BeatmapRow;
use sqlx::{Error as SqlxError, PgPool};

pub async fn find_by_id(pool: &PgPool, id: i32) -> Result<Option<BeatmapRow>, SqlxError> {
sqlx::query_as!(
BeatmapRow,
r#"
SELECT id, osu_id, beatmapset_id, difficulty, count_circles, count_sliders, count_spinners, max_combo, main_pattern, cs, ar, od, hp, mode, status, created_at, updated_at
FROM beatmap
WHERE id = $1
"#,
id
)
.fetch_optional(pool)
.await
}

pub async fn find_by_osu_id(pool: &PgPool, osu_id: i32) -> Result<Option<BeatmapRow>, SqlxError> {
sqlx::query_as!(
BeatmapRow,
r#"
SELECT id, osu_id, beatmapset_id, difficulty, count_circles, count_sliders, count_spinners, max_combo, main_pattern, cs, ar, od, hp, mode, status, created_at, updated_at
FROM beatmap
WHERE osu_id = $1
"#,
osu_id
)
.fetch_optional(pool)
.await
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::define_insert_returning_id;
use crate::models::beatmap::types::BeatmapRow;
use crate::models::beatmap::beatmap::types::BeatmapRow;
// no extra imports needed

define_insert_returning_id!(
insert,
Expand All @@ -8,20 +9,15 @@ define_insert_returning_id!(
osu_id,
beatmapset_id,
difficulty,
difficulty_rating,
count_circles,
count_sliders,
count_spinners,
max_combo,
drain_time,
total_time,
bpm,
main_pattern,
cs,
ar,
od,
hp,
mode,
status,
file_md5,
file_path
status
);
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
pub mod by_id;
pub mod by_score_id;
pub mod insert;

pub use by_id::*;
pub use by_score_id::*;
pub use insert::*;
1 change: 1 addition & 0 deletions src/models/beatmap/beatmap/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

91 changes: 91 additions & 0 deletions src/models/beatmap/beatmap/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use super::validators::{validate_ar, validate_od_hp_cs, validate_status};
use bigdecimal::BigDecimal;
use chrono::NaiveDateTime;
use serde_json::Value;
use validator::Validate;

#[derive(Debug, Clone, sqlx::FromRow, Validate)]
pub struct BeatmapRow {
/// Unique identifier for the beatmap record.
/// Must be a positive integer (≥ 1).
#[validate(range(min = 1, message = "ID must be positive"))]
pub id: i32,

/// Osu beatmap ID from the official osu! API.
/// Must be a positive integer (≥ 1).
#[validate(range(min = 1, message = "Osu ID must be positive"))]
pub osu_id: Option<i32>,

/// Reference to the beatmapset this beatmap belongs to.
/// Optional field, can be None.
pub beatmapset_id: Option<i32>,

/// Difficulty name of the beatmap.
/// Must be between 1 and 255 characters.
#[validate(length(
min = 1,
max = 255,
message = "Difficulty must be between 1 and 255 characters"
))]
pub difficulty: String,

/// Number of circles in the beatmap.
/// Must be a non-negative integer (≥ 0).
#[validate(range(min = 0, message = "Count circles must be non-negative"))]
pub count_circles: i32,

/// Number of sliders in the beatmap.
/// Must be a non-negative integer (≥ 0).
#[validate(range(min = 0, message = "Count sliders must be non-negative"))]
pub count_sliders: i32,

/// Number of spinners in the beatmap.
/// Must be a non-negative integer (≥ 0).
#[validate(range(min = 0, message = "Count spinners must be non-negative"))]
pub count_spinners: i32,

/// Maximum combo possible in the beatmap.
/// Must be a non-negative integer (≥ 0).
#[validate(range(min = 0, message = "Max combo must be non-negative"))]
pub max_combo: i32,

/// Main pattern data stored as JSON.
/// Must not be null.
pub main_pattern: Value,

/// Circle Size (CS) value.
/// Must be between 0.0 and 10.0.
#[validate(custom(function = "validate_od_hp_cs"))]
pub cs: BigDecimal,

/// Approach Rate (AR) value.
/// Must be between 0.0 and 10.0.
#[validate(custom(function = "validate_ar"))]
pub ar: BigDecimal,

/// Overall Difficulty (OD) value.
/// Must be between 0.0 and 10.0.
#[validate(custom(function = "validate_od_hp_cs"))]
pub od: BigDecimal,

/// HP Drain (HP) value.
/// Must be between 0.0 and 10.0.
#[validate(custom(function = "validate_od_hp_cs"))]
pub hp: BigDecimal,

/// Game mode (0=osu!, 1=Taiko, 2=Catch, 3=Mania).
/// Must be between 0 and 3.
#[validate(range(min = 0, max = 3, message = "Mode must be between 0 and 3"))]
pub mode: i32,

/// Status of the beatmap.
/// Must be one of: 'pending', 'ranked', 'qualified', 'loved', 'graveyard'.
#[validate(custom(function = "validate_status"))]
pub status: String,

/// Timestamp when the beatmap was created.
pub created_at: Option<NaiveDateTime>,

/// Timestamp when the beatmap was last updated.
pub updated_at: Option<NaiveDateTime>,
}
23 changes: 23 additions & 0 deletions src/models/beatmap/beatmap/validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use bigdecimal::{BigDecimal, FromPrimitive};
use validator::ValidationError;

pub fn validate_status(status: &str) -> Result<(), ValidationError> {
match status {
"pending" | "ranked" | "qualified" | "loved" | "graveyard" => Ok(()),
_ => Err(validator::ValidationError::new("invalid_status")),
}
}

pub fn validate_od_hp_cs(value: &BigDecimal) -> Result<(), ValidationError> {
if value < &BigDecimal::from_f64(0.0).unwrap() || value > &BigDecimal::from_f64(10.0).unwrap() {
return Err(validator::ValidationError::new("invalid_od_hp_cs"));
}
Ok(())
}

pub fn validate_ar(value: &BigDecimal) -> Result<(), ValidationError> {
if value < &BigDecimal::from_f64(0.0).unwrap() || value > &BigDecimal::from_f64(11.0).unwrap() {
return Err(validator::ValidationError::new("invalid_ar"));
}
Ok(())
}
17 changes: 17 additions & 0 deletions src/models/beatmap/beatmapset/impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use super::query::{find_by_id, find_by_osu_id, insert};
use super::BeatmapsetRow;
use sqlx::{Error as SqlxError, PgPool};

impl BeatmapsetRow {
pub async fn insert(self, pool: &PgPool) -> Result<i32, SqlxError> {
insert(pool, self).await
}

pub async fn find_by_id(pool: &PgPool, id: i32) -> Result<Self, SqlxError> {
find_by_id(pool, id).await?.ok_or(SqlxError::RowNotFound)
}

pub async fn find_by_osu_id(pool: &PgPool, osu_id: i32) -> Result<Option<Self>, SqlxError> {
find_by_osu_id(pool, osu_id).await
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pub mod r#impl;
pub mod query;
pub mod types;
pub(super) mod validators;

#[cfg(test)]
mod tests;
Expand Down
33 changes: 33 additions & 0 deletions src/models/beatmap/beatmapset/query/by_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::models::beatmap::beatmapset::types::BeatmapsetRow;
use sqlx::{Error as SqlxError, PgPool};

pub async fn find_by_id(pool: &PgPool, id: i32) -> Result<Option<BeatmapsetRow>, SqlxError> {
sqlx::query_as!(
BeatmapsetRow,
r#"
SELECT id, osu_id, artist, artist_unicode, title, title_unicode, creator, source, tags, has_video, has_storyboard, is_explicit, is_featured, cover_url, preview_url, osu_file_url, created_at, updated_at
FROM beatmapset
WHERE id = $1
"#,
id
)
.fetch_optional(pool)
.await
}

pub async fn find_by_osu_id(
pool: &PgPool,
osu_id: i32,
) -> Result<Option<BeatmapsetRow>, SqlxError> {
sqlx::query_as!(
BeatmapsetRow,
r#"
SELECT id, osu_id, artist, artist_unicode, title, title_unicode, creator, source, tags, has_video, has_storyboard, is_explicit, is_featured, cover_url, preview_url, osu_file_url, created_at, updated_at
FROM beatmapset
WHERE osu_id = $1
"#,
osu_id
)
.fetch_optional(pool)
.await
}
24 changes: 24 additions & 0 deletions src/models/beatmap/beatmapset/query/insert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::define_insert_returning_id;
use crate::models::beatmap::beatmapset::types::BeatmapsetRow;
// no extra imports needed

define_insert_returning_id!(
insert,
"beatmapset",
BeatmapsetRow,
osu_id,
artist,
artist_unicode,
title,
title_unicode,
creator,
source,
tags,
has_video,
has_storyboard,
is_explicit,
is_featured,
cover_url,
preview_url,
osu_file_url
);
5 changes: 5 additions & 0 deletions src/models/beatmap/beatmapset/query/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod by_id;
pub mod insert;

pub use by_id::*;
pub use insert::*;
1 change: 1 addition & 0 deletions src/models/beatmap/beatmapset/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading