Use streams and such to have 200 results

This commit is contained in:
Natsu Kagami 2025-05-08 12:29:40 -04:00
parent 8fdd576eb9
commit 0661199420
Signed by: nki
GPG key ID: 55A032EB38B49ADB
8 changed files with 150 additions and 86 deletions

View file

@ -5,6 +5,9 @@ use crate::OsuClient;
use rosu_v2::error::OsuError;
use youmubot_prelude::*;
/// Maximum number of scores returned by the osu! api.
pub const SCORE_COUNT_LIMIT: usize = 200;
#[derive(Clone, Debug)]
pub enum UserID {
Username(String),
@ -54,6 +57,7 @@ fn handle_not_found<T>(v: Result<T, OsuError>) -> Result<Option<T>, OsuError> {
}
pub mod builders {
use futures_util::TryStream;
use rosu_v2::model::mods::GameModsIntermode;
use crate::models;
@ -196,7 +200,9 @@ pub mod builders {
}
pub fn limit(&mut self, limit: u8) -> &mut Self {
self.limit = Some(limit).filter(|&v| v <= 100).or(self.limit);
self.limit = Some(limit)
.filter(|&v| v <= SCORE_COUNT_LIMIT as u8)
.or(self.limit);
self
}
@ -237,17 +243,19 @@ pub mod builders {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum UserScoreType {
Recent,
Best,
Pin,
}
#[derive(Debug, Clone)]
pub struct UserScoreRequestBuilder {
score_type: UserScoreType,
user: UserID,
mode: Option<Mode>,
limit: Option<u8>,
limit: Option<usize>,
include_fails: bool,
}
@ -267,8 +275,12 @@ pub mod builders {
self
}
pub fn limit(&mut self, limit: u8) -> &mut Self {
self.limit = Some(limit).filter(|&v| v <= 100).or(self.limit);
pub fn limit(&mut self, limit: usize) -> &mut Self {
self.limit = if limit > SCORE_COUNT_LIMIT {
self.limit
} else {
Some(limit)
};
self
}
@ -277,9 +289,30 @@ pub mod builders {
self
}
pub(crate) async fn build(self, client: &OsuClient) -> Result<Vec<models::Score>> {
async fn with_offset(
self,
offset: Option<usize>,
client: OsuClient,
) -> Result<Option<(Vec<models::Score>, Option<usize>)>> {
const MAXIMUM_LIMIT: usize = 100;
let offset = if let Some(offset) = offset {
offset
} else {
return Ok(None);
};
let count = match self.limit {
Some(limit) => (limit - offset).min(MAXIMUM_LIMIT),
None => MAXIMUM_LIMIT,
};
if count == 0 {
return Ok(None);
}
let scores = handle_not_found({
let mut r = client.rosu.user_scores(self.user);
let mut r = client
.rosu
.user_scores(self.user.clone())
.limit(count)
.offset(offset);
r = match self.score_type {
UserScoreType::Recent => r.recent().include_fails(self.include_fails),
UserScoreType::Best => r.best(),
@ -288,13 +321,29 @@ pub mod builders {
if let Some(mode) = self.mode {
r = r.mode(mode.into());
}
if let Some(limit) = self.limit {
r = r.limit(limit as usize);
}
r.await
})?
.ok_or_else(|| error!("user not found"))?;
Ok(scores.into_iter().map(|v| v.into()).collect())
let count = scores.len();
Ok(Some((
scores.into_iter().map(|v| v.into()).collect(),
if count == MAXIMUM_LIMIT {
Some(offset + MAXIMUM_LIMIT)
} else {
None
},
)))
}
pub(crate) fn build(
self,
client: OsuClient,
) -> impl TryStream<Ok = models::Score, Error = Error> {
futures::stream::try_unfold(Some(0), move |off| {
self.clone().with_offset(off, client.clone())
})
.map_ok(|v| futures::stream::iter(v).map(|v| Ok(v) as Result<_>))
.try_flatten()
}
}
}