Skip to content

Commit eb95673

Browse files
Address pr + improve serialization
1 parent 8f34f14 commit eb95673

File tree

10 files changed

+121
-59
lines changed

10 files changed

+121
-59
lines changed

src/database/repository/mod_versions.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl ModVersionRow {
4343
geode: self.geode,
4444
early_load: self.early_load,
4545
requires_patching: self.requires_patching,
46-
download_count: self.download_count,
46+
download_count: self.download_count.into(),
4747
api: self.api,
4848
mod_id: self.mod_id,
4949
status: self.status,
@@ -224,7 +224,7 @@ pub async fn create_from_json(
224224
download_link: row.download_link,
225225
hash: row.hash,
226226
geode: geode.to_string(),
227-
download_count: 0,
227+
download_count: 0.into(),
228228
early_load: row.early_load,
229229
requires_patching: row.requires_patching,
230230
api: row.api,
@@ -333,7 +333,7 @@ pub async fn update_pending_version(
333333
download_link: row.download_link,
334334
hash: row.hash,
335335
geode: geode.to_string(),
336-
download_count: row.download_count,
336+
download_count: row.download_count.into(),
337337
early_load: row.early_load,
338338
requires_patching: row.requires_patching,
339339
api: row.api,

src/database/repository/mods.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl ModRecordGetOne {
2626
id: self.id,
2727
repository: self.repository,
2828
featured: self.featured,
29-
download_count: self.download_count,
29+
download_count: self.download_count.into(),
3030
versions: Default::default(),
3131
tags: Default::default(),
3232
developers: Default::default(),

src/endpoints/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod developers;
99
pub mod health;
1010
pub mod loader;
1111
pub mod mod_versions;
12+
pub mod mod_status_badge;
1213
pub mod mods;
1314
pub mod stats;
1415
pub mod tags;

src/endpoints/mod_status_badge.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::config::AppData;
22
use crate::endpoints::ApiError;
33
use actix_web::{HttpResponse, Responder, get, web};
44
use serde::Deserialize;
5+
use utoipa::{IntoParams, ToSchema};
56

67
use std::fs;
78
use std::path::Path;
@@ -10,9 +11,18 @@ use urlencoding;
1011
const LABEL_COLOR: &str = "#0c0811";
1112
const STAT_COLOR: &str = "#5f3d84";
1213

13-
#[derive(Deserialize)]
14+
#[derive(Deserialize, Clone, Copy, PartialEq, Eq, ToSchema)]
15+
#[serde(rename_all = "snake_case")]
16+
pub enum StatusBadgeStat {
17+
Version,
18+
GdVersion,
19+
GeodeVersion,
20+
Downloads,
21+
}
22+
23+
#[derive(Deserialize, IntoParams)]
1424
pub struct StatusBadgeQuery {
15-
pub stat: String,
25+
pub stat: StatusBadgeStat,
1626
}
1727

1828
#[utoipa::path(
@@ -21,7 +31,7 @@ pub struct StatusBadgeQuery {
2131
tag = "mods",
2232
params(
2333
("id" = String, Path, description = "Mod ID"),
24-
("stat" = String, Query, description = "Stat to display: version, gd_version, geode_version, downloads")
34+
StatusBadgeQuery
2535
),
2636
responses(
2737
(status = 302, description = "Redirect to Shields.io badge"),
@@ -31,40 +41,36 @@ pub struct StatusBadgeQuery {
3141
)]
3242
#[get("/v1/mods/{id}/status_badge")]
3343
pub async fn status_badge(
34-
_data: web::Data<AppData>,
44+
data: web::Data<AppData>,
3545
id: web::Path<String>,
3646
query: web::Query<StatusBadgeQuery>,
3747
) -> Result<impl Responder, ApiError> {
38-
let (stat, label, svg_path) = match query.stat.as_str() {
39-
"version" => (
48+
let (stat, label, svg_path) = match query.stat {
49+
StatusBadgeStat::Version => (
4050
"payload.versions[0].version",
4151
"Version",
4252
"static/mod_version.svg",
4353
),
44-
"gd_version" => (
54+
StatusBadgeStat::GdVersion => (
4555
"payload.versions[0].gd.win",
4656
"Geometry Dash",
4757
"static/mod_gd_version.svg",
4858
),
49-
"geode_version" => (
59+
StatusBadgeStat::GeodeVersion => (
5060
"payload.versions[0].geode",
5161
"Geode",
5262
"static/mod_geode_version.svg",
5363
),
54-
"downloads" => (
64+
StatusBadgeStat::Downloads => (
5565
"payload.download_count",
5666
"Downloads",
5767
"static/mod_downloads.svg",
5868
),
59-
_ => return Err(ApiError::BadRequest("Invalid stat parameter".into())),
6069
};
6170
let svg = fs::read_to_string(Path::new(svg_path))
6271
.map_err(|_| ApiError::BadRequest(format!("Could not read SVG file: {}", svg_path)))?;
63-
let api_url = format!(
64-
"{}/v1/mods/{}?abbreviate=true",
65-
"http://api.geode-sdk.org", id
66-
);
67-
let mod_link = format!("https://geode-sdk.org/mods/{}", id);
72+
let api_url = format!("{}/v1/mods/{}?abbreviate=true", data.app_url(), id);
73+
let mod_link = format!("{}/mods/{}", data.front_url(), id);
6874
let svg_data_url = format!("data:image/svg+xml;utf8,{}", urlencoding::encode(&svg));
6975
let shields_url = format!(
7076
"https://img.shields.io/badge/dynamic/json?url={}&query={}&label={}&labelColor={}&color={}&link={}&style=plastic&logo={}",

src/endpoints/mods.rs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
pub struct ModGetQueryParams {
33
pub abbreviate: Option<bool>,
44
}
5-
use crate::abbreviate::abbreviate_number;
65
use crate::config::AppData;
76
use crate::database::repository::developers;
87
use crate::database::repository::incompatibilities;
@@ -27,9 +26,9 @@ use crate::types::models::mod_version_status::ModVersionStatusEnum;
2726
use crate::webhook::discord::DiscordWebhook;
2827
use actix_web::{HttpResponse, Responder, get, post, put, web};
2928
use serde::Deserialize;
30-
use serde::Serialize;
3129
use sqlx::Acquire;
3230
use utoipa::{IntoParams, ToSchema};
31+
use serde::Serialize;
3332

3433
#[derive(Deserialize, Default, Hash, Eq, PartialEq, ToSchema)]
3534
#[serde(rename_all = "snake_case")]
@@ -176,31 +175,11 @@ pub async fn get(
176175
i.modify_metadata(data.app_url(), has_extended_permissions);
177176
}
178177

179-
// If abbreviate param is set, abbreviate download_count fields
180-
let mut payload = serde_json::to_value(&the_mod).unwrap();
181-
if query.abbreviate.unwrap_or(false) {
182-
if let Some(obj) = payload.as_object_mut() {
183-
obj.insert(
184-
"download_count".to_string(),
185-
serde_json::Value::String(abbreviate_number(the_mod.download_count)),
186-
);
187-
if let Some(versions) = obj.get_mut("versions").and_then(|v| v.as_array_mut()) {
188-
for (i, v) in versions.iter_mut().enumerate() {
189-
if let Some(version_obj) = v.as_object_mut() {
190-
if let Some(dc) = the_mod.versions.get(i) {
191-
version_obj.insert(
192-
"download_count".to_string(),
193-
serde_json::Value::String(abbreviate_number(dc.download_count)),
194-
);
195-
}
196-
}
197-
}
198-
}
199-
}
200-
}
178+
the_mod.set_abbreviated_download_counts(query.abbreviate.unwrap_or(false));
179+
201180
Ok(web::Json(ApiResponse {
202181
error: "".into(),
203-
payload,
182+
payload: the_mod,
204183
}))
205184
}
206185

src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::endpoints::mod_status_badge::status_badge;
21
use crate::openapi::ApiDoc;
32
use crate::types::api;
43
use actix_cors::Cors;
@@ -66,7 +65,7 @@ async fn main() -> anyhow::Result<()> {
6665
.service(endpoints::mods::create)
6766
.service(endpoints::mods::update_mod)
6867
.service(endpoints::mods::get_logo)
69-
.service(status_badge)
68+
.service(endpoints::mod_status_badge::status_badge)
7069
.service(endpoints::mod_versions::get_version_index)
7170
.service(endpoints::mod_versions::get_one)
7271
.service(endpoints::mod_versions::download_version)

src/types/models/download_count.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::abbreviate::abbreviate_number;
2+
use serde::{Serialize, Serializer};
3+
4+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
5+
pub struct DownloadCount {
6+
count: i32,
7+
abbreviate: bool,
8+
}
9+
10+
impl DownloadCount {
11+
pub const fn new(count: i32) -> Self {
12+
Self {
13+
count,
14+
abbreviate: false,
15+
}
16+
}
17+
18+
pub fn set_abbreviated(&mut self, abbreviate: bool) {
19+
self.abbreviate = abbreviate;
20+
}
21+
}
22+
23+
impl From<i32> for DownloadCount {
24+
fn from(count: i32) -> Self {
25+
Self::new(count)
26+
}
27+
}
28+
29+
impl Serialize for DownloadCount {
30+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31+
where
32+
S: Serializer,
33+
{
34+
if self.abbreviate {
35+
serializer.serialize_str(&abbreviate_number(self.count))
36+
} else {
37+
serializer.serialize_i32(self.count)
38+
}
39+
}
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use super::DownloadCount;
45+
46+
#[test]
47+
fn serializes_as_number_by_default() {
48+
let serialized = serde_json::to_string(&DownloadCount::new(1234)).unwrap();
49+
50+
assert_eq!(serialized, "1234");
51+
}
52+
53+
#[test]
54+
fn serializes_as_abbreviated_string_when_enabled() {
55+
let mut count = DownloadCount::new(1234);
56+
count.set_abbreviated(true);
57+
let serialized = serde_json::to_string(&count).unwrap();
58+
59+
assert_eq!(serialized, "\"1.2K\"");
60+
}
61+
}

src/types/models/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod dependency;
2+
pub mod download_count;
23
pub mod developer;
34
pub mod github_login_attempt;
45
pub mod incompatibility;

src/types/models/mod_entity.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::{
22
dependency::ResponseDependency,
3+
download_count::DownloadCount,
34
developer::ModDeveloper,
45
incompatibility::{Replacement, ResponseIncompatibility},
56
mod_gd_version::{DetailedGDVersion, GDVersionEnum, ModGDVersion, VerPlatform},
@@ -22,20 +23,21 @@ use crate::{
2223
},
2324
};
2425
use semver::Version;
25-
use serde::Serialize;
26-
use utoipa::ToSchema;
2726
use sqlx::{
2827
PgConnection,
2928
types::chrono::{DateTime, Utc},
3029
};
3130
use std::collections::HashMap;
31+
use serde::Serialize;
32+
use utoipa::ToSchema;
3233

33-
#[derive(Serialize, Debug, Clone, sqlx::FromRow, ToSchema)]
34+
#[derive(Serialize, Debug, Clone, ToSchema)]
3435
pub struct Mod {
3536
pub id: String,
3637
pub repository: Option<String>,
3738
pub featured: bool,
38-
pub download_count: i32,
39+
#[schema(value_type = i32)]
40+
pub download_count: DownloadCount,
3941
pub developers: Vec<ModDeveloper>,
4042
pub versions: Vec<ModVersion>,
4143
pub tags: Vec<String>,
@@ -107,6 +109,13 @@ pub struct ModStats {
107109
}
108110

109111
impl Mod {
112+
pub fn set_abbreviated_download_counts(&mut self, abbreviate: bool) {
113+
self.download_count.set_abbreviated(abbreviate);
114+
for version in &mut self.versions {
115+
version.set_abbreviated_download_count(abbreviate);
116+
}
117+
}
118+
110119
pub async fn get_stats(pool: &mut PgConnection) -> Result<ModStats, DatabaseError> {
111120
let result = sqlx::query!(
112121
"
@@ -356,7 +365,7 @@ impl Mod {
356365
Mod {
357366
id: x.id,
358367
repository: x.repository,
359-
download_count: x.download_count,
368+
download_count: x.download_count.into(),
360369
featured: x.featured,
361370
versions: vec![version],
362371
tags,
@@ -403,7 +412,7 @@ impl Mod {
403412
Mod {
404413
id: x.id.clone(),
405414
repository: x.repository.clone(),
406-
download_count: x.download_count,
415+
download_count: x.download_count.into(),
407416
featured: x.featured,
408417
versions: version,
409418
tags,
@@ -548,7 +557,7 @@ impl Mod {
548557
description: x.description.clone(),
549558
version: x.version.clone(),
550559
download_link: x.download_link.clone(),
551-
download_count: x.mod_version_download_count,
560+
download_count: x.mod_version_download_count.into(),
552561
hash: x.hash.clone(),
553562
geode: x.geode.clone(),
554563
early_load: x.early_load,
@@ -592,7 +601,7 @@ impl Mod {
592601
id: records[0].id.clone(),
593602
repository: records[0].repository.clone(),
594603
featured: records[0].featured,
595-
download_count: records[0].mod_download_count,
604+
download_count: records[0].mod_download_count.into(),
596605
versions,
597606
tags,
598607
developers: devs,

0 commit comments

Comments
 (0)