You've already forked AstralRinth
forked from didirus/AstralRinth
chore(clippy): enable and fix many stricter lints (#3783)
* chore(clippy): enable and fix many stricter lints These ensure that the codebase uses more idiomatic, performant, and concise language constructions. * chore: make non-Clippy compiler warnings also deny by default
This commit is contained in:
committed by
GitHub
parent
301967d204
commit
f84f8c1c2b
@@ -100,10 +100,7 @@ pub async fn filter_visible_project_ids(
|
||||
project.status.is_searchable()
|
||||
} else {
|
||||
!project.status.is_hidden()
|
||||
}) || user_option
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
.unwrap_or(false)
|
||||
}) || user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||
{
|
||||
return_projects.push(project.id);
|
||||
} else if user_option.is_some() {
|
||||
@@ -158,7 +155,7 @@ pub async fn filter_enlisted_projects_ids(
|
||||
)
|
||||
.fetch(pool)
|
||||
.map_ok(|row| {
|
||||
for x in projects.iter() {
|
||||
for x in &projects {
|
||||
let bool =
|
||||
Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id;
|
||||
if bool {
|
||||
@@ -238,7 +235,6 @@ pub async fn filter_visible_version_ids(
|
||||
redis: &RedisPool,
|
||||
) -> Result<Vec<crate::database::models::DBVersionId>, ApiError> {
|
||||
let mut return_versions = Vec::new();
|
||||
let mut check_versions = Vec::new();
|
||||
|
||||
// First, filter out versions belonging to projects we can't see
|
||||
// (ie: a hidden project, but public version, should still be hidden)
|
||||
@@ -271,15 +267,10 @@ pub async fn filter_visible_version_ids(
|
||||
// - we are enlisted on the team of the mod
|
||||
if (!version.status.is_hidden()
|
||||
&& visible_project_ids.contains(&version.project_id))
|
||||
|| user_option
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
.unwrap_or(false)
|
||||
|| user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||
|| enlisted_version_ids.contains(&version.id)
|
||||
{
|
||||
return_versions.push(version.id);
|
||||
} else if user_option.is_some() {
|
||||
check_versions.push(version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,10 +301,7 @@ pub async fn filter_enlisted_version_ids(
|
||||
.await?;
|
||||
|
||||
for version in versions {
|
||||
if user_option
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
.unwrap_or(false)
|
||||
if user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||
|| (user_option.is_some()
|
||||
&& authorized_project_ids.contains(&version.project_id))
|
||||
{
|
||||
@@ -349,10 +337,7 @@ pub async fn filter_visible_collections(
|
||||
|
||||
for collection in collections {
|
||||
if !collection.status.is_hidden()
|
||||
|| user_option
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
.unwrap_or(false)
|
||||
|| user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||
{
|
||||
return_collections.push(collection.into());
|
||||
} else if user_option.is_some() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::auth::oauth::uris::{OAuthRedirectUris, ValidatedRedirectUri};
|
||||
use crate::auth::validate::extract_authorization_header;
|
||||
@@ -17,7 +19,7 @@ use crate::queue::session::AuthQueue;
|
||||
use actix_web::http::header::{CACHE_CONTROL, LOCATION, PRAGMA};
|
||||
use actix_web::web::{Data, Query, ServiceConfig};
|
||||
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
||||
use chrono::Duration;
|
||||
use chrono::{DateTime, Duration};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
@@ -280,8 +282,8 @@ pub async fn request_token(
|
||||
authorization_id,
|
||||
token_hash,
|
||||
scopes,
|
||||
created: Default::default(),
|
||||
expires: Default::default(),
|
||||
created: DateTime::default(),
|
||||
expires: DateTime::default(),
|
||||
last_used: None,
|
||||
client_id,
|
||||
user_id,
|
||||
@@ -456,7 +458,7 @@ fn append_params_to_uri(uri: &str, params: &[impl AsRef<str>]) -> String {
|
||||
let mut uri = uri.to_string();
|
||||
let mut connector = if uri.contains('?') { "&" } else { "?" };
|
||||
for param in params {
|
||||
uri.push_str(&format!("{}{}", connector, param.as_ref()));
|
||||
write!(&mut uri, "{connector}{}", param.as_ref()).unwrap();
|
||||
connector = "&";
|
||||
}
|
||||
|
||||
|
||||
@@ -93,12 +93,11 @@ where
|
||||
.await?;
|
||||
|
||||
let rate_limit_ignore = dotenvy::var("RATE_LIMIT_IGNORE_KEY")?;
|
||||
if !req
|
||||
if req
|
||||
.headers()
|
||||
.get("x-ratelimit-key")
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.map(|x| x == rate_limit_ignore)
|
||||
.unwrap_or(false)
|
||||
.is_none_or(|x| x != rate_limit_ignore)
|
||||
{
|
||||
let metadata = get_session_metadata(req).await?;
|
||||
session_queue.add_session(session.id, metadata).await;
|
||||
@@ -130,7 +129,7 @@ where
|
||||
|
||||
user.map(|u| (access_token.scopes, u))
|
||||
}
|
||||
Some(("github", _)) | Some(("gho", _)) | Some(("ghp", _)) => {
|
||||
Some(("github" | "gho" | "ghp", _)) => {
|
||||
let user = AuthProvider::GitHub.get_user(token).await?;
|
||||
let id =
|
||||
AuthProvider::GitHub.get_user_id(&user.id, executor).await?;
|
||||
|
||||
@@ -105,7 +105,7 @@ impl DBFriend {
|
||||
created: row.created,
|
||||
accepted: row.accepted,
|
||||
})
|
||||
.filter(|x| accepted.map(|y| y == x.accepted).unwrap_or(true))
|
||||
.filter(|x| accepted.is_none_or(|y| y == x.accepted))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(friends)
|
||||
|
||||
@@ -675,7 +675,7 @@ impl LoaderFieldEnumValue {
|
||||
.into_iter()
|
||||
.filter(|x| {
|
||||
let mut bool = true;
|
||||
for (key, value) in filter.iter() {
|
||||
for (key, value) in &filter {
|
||||
if let Some(metadata_value) = x.metadata.get(key) {
|
||||
bool &= metadata_value == value;
|
||||
} else {
|
||||
@@ -713,7 +713,7 @@ impl VersionField {
|
||||
query_version_fields.push(base.clone().with_string_value(s))
|
||||
}
|
||||
VersionFieldValue::Boolean(b) => query_version_fields
|
||||
.push(base.clone().with_int_value(if b { 1 } else { 0 })),
|
||||
.push(base.clone().with_int_value(b as i32)),
|
||||
VersionFieldValue::ArrayInteger(v) => {
|
||||
for i in v {
|
||||
query_version_fields
|
||||
@@ -728,9 +728,8 @@ impl VersionField {
|
||||
}
|
||||
VersionFieldValue::ArrayBoolean(v) => {
|
||||
for b in v {
|
||||
query_version_fields.push(
|
||||
base.clone().with_int_value(if b { 1 } else { 0 }),
|
||||
);
|
||||
query_version_fields
|
||||
.push(base.clone().with_int_value(b as i32));
|
||||
}
|
||||
}
|
||||
VersionFieldValue::Enum(_, v) => query_version_fields
|
||||
@@ -757,7 +756,7 @@ impl VersionField {
|
||||
l.field_id.0,
|
||||
l.version_id.0,
|
||||
l.int_value,
|
||||
l.enum_value.as_ref().map(|e| e.0).unwrap_or(-1),
|
||||
l.enum_value.as_ref().map_or(-1, |e| e.0),
|
||||
l.string_value.clone(),
|
||||
)
|
||||
})
|
||||
@@ -849,12 +848,11 @@ impl VersionField {
|
||||
query_loader_fields
|
||||
.iter()
|
||||
.flat_map(|q| {
|
||||
let loader_field_type = match LoaderFieldType::build(
|
||||
let Some(loader_field_type) = LoaderFieldType::build(
|
||||
&q.field_type,
|
||||
q.enum_type.map(|l| l.0),
|
||||
) {
|
||||
Some(lft) => lft,
|
||||
None => return vec![],
|
||||
) else {
|
||||
return vec![];
|
||||
};
|
||||
let loader_field = LoaderField {
|
||||
id: q.id,
|
||||
@@ -1085,23 +1083,17 @@ impl VersionFieldValue {
|
||||
};
|
||||
|
||||
// Check errors- version_id must all be the same
|
||||
// If the field type is a non-array, then the reason for multiple version ids is that there are multiple versions being aggregated, and those version ids are contained within.
|
||||
// If the field type is an array, then the reason for multiple version ids is that there are multiple values for a single version
|
||||
// (or a greater aggregation between multiple arrays, in which case the per-field version is lost, so we just take the first one and use it for that)
|
||||
let version_id = qvfs
|
||||
.iter()
|
||||
.map(|qvf| qvf.version_id)
|
||||
.unique()
|
||||
.collect::<Vec<_>>();
|
||||
// If the field type is a non-array, then the reason for multiple version ids is that there are multiple versions being aggregated, and those version ids are contained within.
|
||||
// If the field type is an array, then the reason for multiple version ids is that there are multiple values for a single version
|
||||
// (or a greater aggregation between multiple arrays, in which case the per-field version is lost, so we just take the first one and use it for that)
|
||||
let version_id =
|
||||
version_id.into_iter().next().unwrap_or(DBVersionId(0));
|
||||
.next()
|
||||
.unwrap_or(DBVersionId(0));
|
||||
|
||||
let field_id = qvfs
|
||||
.iter()
|
||||
.map(|qvf| qvf.field_id)
|
||||
.unique()
|
||||
.collect::<Vec<_>>();
|
||||
if field_id.len() > 1 {
|
||||
if qvfs.iter().map(|qvf| qvf.field_id).unique().count() > 1 {
|
||||
return Err(DatabaseError::SchemaError(format!(
|
||||
"Multiple field ids for field {field_name}"
|
||||
)));
|
||||
@@ -1274,7 +1266,7 @@ impl VersionFieldValue {
|
||||
};
|
||||
|
||||
// Sort arrayenums by ordering, then by created
|
||||
for (_, v) in value.iter_mut() {
|
||||
for (_, v) in &mut value {
|
||||
if let VersionFieldValue::ArrayEnum(_, v) = v {
|
||||
v.sort_by(|a, b| {
|
||||
a.ordering.cmp(&b.ordering).then(a.created.cmp(&b.created))
|
||||
@@ -1317,8 +1309,8 @@ impl VersionFieldValue {
|
||||
}
|
||||
}
|
||||
|
||||
// For conversion to an interanl string(s), such as for search facets, filtering, or direct hardcoding
|
||||
// No matter the type, it will be converted to a Vec<String>, whre the non-array types will have a single element
|
||||
// For conversion to an internal string(s), such as for search facets, filtering, or direct hardcoding
|
||||
// No matter the type, it will be converted to a Vec<String>, where the non-array types will have a single element
|
||||
pub fn as_strings(&self) -> Vec<String> {
|
||||
match self {
|
||||
VersionFieldValue::Integer(i) => vec![i.to_string()],
|
||||
@@ -1343,22 +1335,19 @@ impl VersionFieldValue {
|
||||
VersionFieldValue::Integer(i) => value.as_i64() == Some(*i as i64),
|
||||
VersionFieldValue::Text(s) => value.as_str() == Some(s),
|
||||
VersionFieldValue::Boolean(b) => value.as_bool() == Some(*b),
|
||||
VersionFieldValue::ArrayInteger(v) => value
|
||||
.as_i64()
|
||||
.map(|i| v.contains(&(i as i32)))
|
||||
.unwrap_or(false),
|
||||
VersionFieldValue::ArrayText(v) => value
|
||||
.as_str()
|
||||
.map(|s| v.contains(&s.to_string()))
|
||||
.unwrap_or(false),
|
||||
VersionFieldValue::ArrayInteger(v) => {
|
||||
value.as_i64().is_some_and(|i| v.contains(&(i as i32)))
|
||||
}
|
||||
VersionFieldValue::ArrayText(v) => {
|
||||
value.as_str().is_some_and(|s| v.contains(&s.to_string()))
|
||||
}
|
||||
VersionFieldValue::ArrayBoolean(v) => {
|
||||
value.as_bool().map(|b| v.contains(&b)).unwrap_or(false)
|
||||
value.as_bool().is_some_and(|b| v.contains(&b))
|
||||
}
|
||||
VersionFieldValue::Enum(_, v) => value.as_str() == Some(&v.value),
|
||||
VersionFieldValue::ArrayEnum(_, v) => value
|
||||
.as_str()
|
||||
.map(|s| v.iter().any(|v| v.value == s))
|
||||
.unwrap_or(false),
|
||||
.is_some_and(|s| v.iter().any(|v| v.value == s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ impl DBOrganization {
|
||||
|ids| async move {
|
||||
let org_ids: Vec<i64> = ids
|
||||
.iter()
|
||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.map(|x| x as i64)
|
||||
.collect();
|
||||
let slugs = ids
|
||||
|
||||
@@ -108,7 +108,7 @@ impl DBPersonalAccessToken {
|
||||
|ids| async move {
|
||||
let pat_ids: Vec<i64> = ids
|
||||
.iter()
|
||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.map(|x| x as i64)
|
||||
.collect();
|
||||
let slugs = ids.into_iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
||||
|
||||
@@ -545,7 +545,7 @@ impl DBProject {
|
||||
let mut exec = exec.acquire().await?;
|
||||
let project_ids_parsed: Vec<i64> = ids
|
||||
.iter()
|
||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.map(|x| x as i64)
|
||||
.collect();
|
||||
let slugs = ids
|
||||
@@ -723,7 +723,7 @@ impl DBProject {
|
||||
|
||||
// Add loader fields to the set we need to fetch
|
||||
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
||||
for loader_field_id in loader_loader_field_ids.iter() {
|
||||
for loader_field_id in &loader_loader_field_ids {
|
||||
loader_field_ids.insert(*loader_field_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ impl DBSession {
|
||||
|ids| async move {
|
||||
let session_ids: Vec<i64> = ids
|
||||
.iter()
|
||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.map(|x| x as i64)
|
||||
.collect();
|
||||
let slugs = ids
|
||||
|
||||
@@ -45,7 +45,7 @@ impl TeamBuilder {
|
||||
.await?;
|
||||
|
||||
let mut team_member_ids = Vec::new();
|
||||
for _ in self.members.iter() {
|
||||
for _ in &self.members {
|
||||
team_member_ids.push(generate_team_member_id(transaction).await?.0);
|
||||
}
|
||||
let TeamBuilder { members } = self;
|
||||
|
||||
@@ -163,7 +163,7 @@ impl DBUser {
|
||||
|ids| async move {
|
||||
let user_ids: Vec<i64> = ids
|
||||
.iter()
|
||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||
.map(|x| x as i64)
|
||||
.collect();
|
||||
let slugs = ids
|
||||
|
||||
@@ -52,7 +52,7 @@ impl DependencyBuilder {
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let mut project_ids = Vec::new();
|
||||
for dependency in builders.iter() {
|
||||
for dependency in &builders {
|
||||
project_ids.push(
|
||||
dependency
|
||||
.try_get_project_id(transaction)
|
||||
@@ -333,9 +333,7 @@ impl DBVersion {
|
||||
) -> Result<Option<()>, DatabaseError> {
|
||||
let result = Self::get(id, &mut **transaction, redis).await?;
|
||||
|
||||
let result = if let Some(result) = result {
|
||||
result
|
||||
} else {
|
||||
let Some(result) = result else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
@@ -550,7 +548,7 @@ impl DBVersion {
|
||||
|
||||
// Add loader fields to the set we need to fetch
|
||||
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
||||
for loader_field_id in loader_loader_field_ids.iter() {
|
||||
for loader_field_id in &loader_loader_field_ids {
|
||||
loader_field_ids.insert(*loader_field_id);
|
||||
}
|
||||
|
||||
@@ -756,7 +754,7 @@ impl DBVersion {
|
||||
let mut files = files.into_iter().map(|x| {
|
||||
let mut file_hashes = HashMap::new();
|
||||
|
||||
for hash in hashes.iter() {
|
||||
for hash in &hashes {
|
||||
if hash.file_id == x.id {
|
||||
file_hashes.insert(
|
||||
hash.algorithm.clone(),
|
||||
@@ -852,7 +850,7 @@ impl DBVersion {
|
||||
ORDER BY v.date_published
|
||||
",
|
||||
algorithm,
|
||||
&file_ids.into_iter().flat_map(|x| x.split('_').last().map(|x| x.as_bytes().to_vec())).collect::<Vec<_>>(),
|
||||
&file_ids.into_iter().filter_map(|x| x.split('_').last().map(|x| x.as_bytes().to_vec())).collect::<Vec<_>>(),
|
||||
)
|
||||
.fetch(executor)
|
||||
.try_fold(DashMap::new(), |acc, f| {
|
||||
@@ -1042,14 +1040,14 @@ mod tests {
|
||||
date_published,
|
||||
project_id: DBProjectId(0),
|
||||
author_id: DBUserId(0),
|
||||
name: Default::default(),
|
||||
version_number: Default::default(),
|
||||
changelog: Default::default(),
|
||||
downloads: Default::default(),
|
||||
version_type: Default::default(),
|
||||
featured: Default::default(),
|
||||
name: String::new(),
|
||||
version_number: String::new(),
|
||||
changelog: String::new(),
|
||||
downloads: 0,
|
||||
version_type: String::new(),
|
||||
featured: false,
|
||||
status: VersionStatus::Listed,
|
||||
requested_status: Default::default(),
|
||||
requested_status: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use ariadne::ids::base62_impl::{parse_base62, to_base62};
|
||||
use chrono::{TimeZone, Utc};
|
||||
use dashmap::DashMap;
|
||||
use deadpool_redis::{Config, Runtime};
|
||||
use futures::future::Either;
|
||||
use prometheus::{IntGauge, Registry};
|
||||
use redis::{Cmd, ExistenceCheck, SetExpiry, SetOptions, cmd};
|
||||
use serde::de::DeserializeOwned;
|
||||
@@ -11,7 +12,6 @@ use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::future::Future;
|
||||
use std::hash::Hash;
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
|
||||
const DEFAULT_EXPIRY: i64 = 60 * 60 * 12; // 12 hours
|
||||
@@ -378,22 +378,10 @@ impl RedisPool {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let mut fetch_tasks: Vec<
|
||||
Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<
|
||||
HashMap<K, RedisValue<T, K, S>>,
|
||||
DatabaseError,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
> = Vec::new();
|
||||
let mut fetch_tasks = Vec::new();
|
||||
|
||||
if !ids.is_empty() {
|
||||
fetch_tasks.push(Box::pin(async {
|
||||
fetch_tasks.push(Either::Left(async {
|
||||
let fetch_ids =
|
||||
ids.iter().map(|x| x.value().clone()).collect::<Vec<_>>();
|
||||
|
||||
@@ -491,7 +479,7 @@ impl RedisPool {
|
||||
}
|
||||
|
||||
if !subscribe_ids.is_empty() {
|
||||
fetch_tasks.push(Box::pin(async {
|
||||
fetch_tasks.push(Either::Right(async {
|
||||
let mut interval =
|
||||
tokio::time::interval(Duration::from_millis(100));
|
||||
let start = Utc::now();
|
||||
|
||||
@@ -313,13 +313,16 @@ pub fn app_config(
|
||||
.app_data(labrinth_config.automated_moderation_queue.clone())
|
||||
.app_data(web::Data::new(labrinth_config.stripe_client.clone()))
|
||||
.app_data(labrinth_config.rate_limiter.clone())
|
||||
.configure(
|
||||
#[allow(unused_variables)]
|
||||
|cfg| {
|
||||
#[cfg(target_os = "linux")]
|
||||
routes::debug::config(cfg)
|
||||
},
|
||||
)
|
||||
.configure({
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
|cfg| routes::debug::config(cfg)
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
|_cfg| ()
|
||||
}
|
||||
})
|
||||
.configure(routes::v2::config)
|
||||
.configure(routes::v3::config)
|
||||
.configure(routes::internal::config)
|
||||
|
||||
@@ -8,6 +8,7 @@ use labrinth::file_hosting::S3Host;
|
||||
use labrinth::search;
|
||||
use labrinth::util::ratelimit::rate_limit_middleware;
|
||||
use labrinth::{check_env_vars, clickhouse, database, file_hosting, queue};
|
||||
use std::ffi::CStr;
|
||||
use std::sync::Arc;
|
||||
use tracing::{error, info};
|
||||
use tracing_actix_web::TracingLogger;
|
||||
@@ -16,10 +17,8 @@ use tracing_actix_web::TracingLogger;
|
||||
#[global_allocator]
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[unsafe(export_name = "malloc_conf")]
|
||||
pub static malloc_conf: &[u8] =
|
||||
b"prof:true,prof_active:true,lg_prof_sample:19\0";
|
||||
pub static MALLOC_CONF: &CStr = c"prof:true,prof_active:true,lg_prof_sample:19";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Pepper {
|
||||
|
||||
@@ -122,7 +122,7 @@ pub fn from_duplicate_version_fields(
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
for (_, v) in fields.iter_mut() {
|
||||
for v in fields.values_mut() {
|
||||
*v = mem::take(v).into_iter().unique().collect_vec();
|
||||
}
|
||||
fields
|
||||
|
||||
@@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize};
|
||||
use sha1::Digest;
|
||||
use sqlx::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::time::Duration;
|
||||
use zip::ZipArchive;
|
||||
@@ -33,29 +34,31 @@ impl ModerationMessages {
|
||||
}
|
||||
|
||||
pub fn markdown(&self, auto_mod: bool) -> String {
|
||||
let mut str = "".to_string();
|
||||
let mut str = String::new();
|
||||
|
||||
for message in &self.messages {
|
||||
str.push_str(&format!("## {}\n", message.header()));
|
||||
str.push_str(&format!("{}\n", message.body()));
|
||||
str.push('\n');
|
||||
write!(&mut str, "## {}\n{}\n\n", message.header(), message.body())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (version_num, messages) in &self.version_specific {
|
||||
for message in messages {
|
||||
str.push_str(&format!(
|
||||
"## Version {}: {}\n",
|
||||
write!(
|
||||
&mut str,
|
||||
"### Version {}: {}\n{}\n\n",
|
||||
version_num,
|
||||
message.header()
|
||||
));
|
||||
str.push_str(&format!("{}\n", message.body()));
|
||||
str.push('\n');
|
||||
message.header(),
|
||||
message.body()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if auto_mod {
|
||||
str.push_str("<hr />\n\n");
|
||||
str.push_str("🤖 This is an automated message generated by AutoMod (BETA). If you are facing issues, please [contact support](https://support.modrinth.com).");
|
||||
str.push_str(
|
||||
"<hr />\n\n\
|
||||
🤖 This is an automated message generated by AutoMod (BETA). If you are facing issues, please [contact support](https://support.modrinth.com)."
|
||||
);
|
||||
}
|
||||
|
||||
str
|
||||
@@ -146,14 +149,13 @@ impl ModerationMessage {
|
||||
match self {
|
||||
ModerationMessage::NoPrimaryFile => "Please attach a file to this version. All files on Modrinth must have files associated with their versions.\n".to_string(),
|
||||
ModerationMessage::PackFilesNotAllowed { files, .. } => {
|
||||
let mut str = "".to_string();
|
||||
str.push_str("This pack redistributes copyrighted material. Please refer to [Modrinth's guide on obtaining modpack permissions](https://support.modrinth.com/en/articles/8797527-obtaining-modpack-permissions) for more information.\n\n");
|
||||
let mut str = String::from("This pack redistributes copyrighted material. Please refer to [Modrinth's guide on obtaining modpack permissions](https://support.modrinth.com/en/articles/8797527-obtaining-modpack-permissions) for more information.\n\n");
|
||||
|
||||
let mut attribute_mods = Vec::new();
|
||||
let mut no_mods = Vec::new();
|
||||
let mut permanent_no_mods = Vec::new();
|
||||
let mut unidentified_mods = Vec::new();
|
||||
for (_, approval) in files.iter() {
|
||||
for approval in files.values() {
|
||||
match approval.status {
|
||||
ApprovalType::Yes | ApprovalType::WithAttributionAndSource => {}
|
||||
ApprovalType::WithAttribution => attribute_mods.push(&approval.file_name),
|
||||
@@ -166,7 +168,7 @@ impl ModerationMessage {
|
||||
fn print_mods(projects: Vec<&String>, headline: &str, val: &mut String) {
|
||||
if projects.is_empty() { return }
|
||||
|
||||
val.push_str(&format!("{headline}\n\n"));
|
||||
write!(val, "{headline}\n\n").unwrap();
|
||||
|
||||
for project in &projects {
|
||||
let additional_text = if project.contains("ftb-quests") {
|
||||
@@ -181,11 +183,11 @@ impl ModerationMessage {
|
||||
None
|
||||
};
|
||||
|
||||
val.push_str(&if let Some(additional_text) = additional_text {
|
||||
format!("- {project} (consider using [{}](https://modrinth.com/project/{}) instead)\n", additional_text.0, additional_text.1)
|
||||
if let Some(additional_text) = additional_text {
|
||||
writeln!(val, "- {project} (consider using [{}](https://modrinth.com/project/{}) instead)", additional_text.0, additional_text.1).unwrap();
|
||||
} else {
|
||||
format!("- {project}\n")
|
||||
})
|
||||
writeln!(val, "- {project}").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if !projects.is_empty() {
|
||||
@@ -278,10 +280,7 @@ impl AutomatedModerationQueue {
|
||||
let mut zip = ZipArchive::new(reader)?;
|
||||
|
||||
let pack: PackFormat = {
|
||||
let mut file =
|
||||
if let Ok(file) = zip.by_name("modrinth.index.json") {
|
||||
file
|
||||
} else {
|
||||
let Ok(mut file) = zip.by_name("modrinth.index.json") else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -301,7 +300,7 @@ impl AutomatedModerationQueue {
|
||||
.files
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|x| {
|
||||
.filter_map(|x| {
|
||||
let hash = x.hashes.get(&PackFileHash::Sha1);
|
||||
|
||||
if let Some(hash) = hash {
|
||||
@@ -397,8 +396,8 @@ impl AutomatedModerationQueue {
|
||||
",
|
||||
serde_json::to_value(&MissingMetadata {
|
||||
identified: final_hashes,
|
||||
flame_files: Default::default(),
|
||||
unknown_files: Default::default(),
|
||||
flame_files: HashMap::new(),
|
||||
unknown_files: HashMap::new(),
|
||||
})?,
|
||||
primary_file.id.0
|
||||
)
|
||||
@@ -432,8 +431,8 @@ impl AutomatedModerationQueue {
|
||||
if hashes.is_empty() {
|
||||
let metadata = MissingMetadata {
|
||||
identified: final_hashes,
|
||||
flame_files: Default::default(),
|
||||
unknown_files: Default::default(),
|
||||
flame_files: HashMap::new(),
|
||||
unknown_files: HashMap::new(),
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
@@ -533,8 +532,8 @@ impl AutomatedModerationQueue {
|
||||
if hashes.is_empty() {
|
||||
let metadata = MissingMetadata {
|
||||
identified: final_hashes,
|
||||
flame_files: Default::default(),
|
||||
unknown_files: Default::default(),
|
||||
flame_files: HashMap::new(),
|
||||
unknown_files: HashMap::new(),
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
@@ -622,8 +621,7 @@ impl AutomatedModerationQueue {
|
||||
|
||||
if !mod_messages.is_empty() {
|
||||
let first_time = database::models::DBThread::get(project.thread_id, &pool).await?
|
||||
.map(|x| x.messages.iter().all(|x| x.author_id == Some(database::models::DBUserId(AUTOMOD_ID)) || x.hide_identity))
|
||||
.unwrap_or(true);
|
||||
.is_none_or(|x| x.messages.iter().all(|x| x.author_id == Some(database::models::DBUserId(AUTOMOD_ID)) || x.hide_identity));
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
let id = ThreadMessageBuilder {
|
||||
@@ -733,10 +731,12 @@ impl AutomatedModerationQueue {
|
||||
if let Err(err) = res {
|
||||
let err = err.as_api_error();
|
||||
|
||||
let mut str = String::new();
|
||||
str.push_str("## Internal AutoMod Error\n\n");
|
||||
str.push_str(&format!("Error code: {}\n\n", err.error));
|
||||
str.push_str(&format!("Error description: {}\n\n", err.description));
|
||||
let str = format!(
|
||||
"## Internal AutoMod Error\n\n\
|
||||
Error code: {}\n\n\
|
||||
Error description: {}\n\n",
|
||||
err.error, err.description
|
||||
);
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
ThreadMessageBuilder {
|
||||
|
||||
@@ -441,8 +441,8 @@ impl PayoutsQueue {
|
||||
}
|
||||
} else {
|
||||
PayoutMethodFee {
|
||||
percentage: Default::default(),
|
||||
min: Default::default(),
|
||||
percentage: Decimal::default(),
|
||||
min: Decimal::default(),
|
||||
max: None,
|
||||
}
|
||||
},
|
||||
@@ -833,7 +833,7 @@ pub async fn process_payout(
|
||||
.map(|x| (x.project_id, x.page_views))
|
||||
.collect::<HashMap<u64, u64>>();
|
||||
|
||||
for (key, value) in downloads_values.iter() {
|
||||
for (key, value) in &downloads_values {
|
||||
let counter = views_values.entry(*key).or_insert(0);
|
||||
*counter += *value;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ pub async fn playtime_ingest(
|
||||
version_id: version.inner.id.0 as u64,
|
||||
loader: playtime.loader,
|
||||
game_version: playtime.game_version,
|
||||
parent: playtime.parent.map(|x| x.0).unwrap_or(0),
|
||||
parent: playtime.parent.map_or(0, |x| x.0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use actix_web::{HttpRequest, HttpResponse, get, patch, post, web};
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
@@ -221,9 +222,11 @@ pub async fn delphi_result_ingest(
|
||||
|
||||
for (issue, trace) in &body.issues {
|
||||
for (path, code) in trace {
|
||||
header.push_str(&format!(
|
||||
write!(
|
||||
&mut header,
|
||||
"\n issue {issue} found at file {path}: \n ```\n{code}\n```"
|
||||
));
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,12 +247,15 @@ pub async fn delphi_result_ingest(
|
||||
|
||||
for (issue, trace) in &body.issues {
|
||||
for path in trace.keys() {
|
||||
thread_header
|
||||
.push_str(&format!("\n\n- issue {issue} found at file {path}"));
|
||||
write!(
|
||||
&mut thread_header,
|
||||
"\n\n- issue {issue} found at file {path}"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if trace.is_empty() {
|
||||
thread_header.push_str(&format!("\n\n- issue {issue} found",));
|
||||
write!(&mut thread_header, "\n\n- issue {issue} found").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -531,11 +531,9 @@ pub async fn edit_subscription(
|
||||
|
||||
if let Some(payment_method) = &edit_subscription.payment_method
|
||||
{
|
||||
let payment_method_id = if let Ok(id) =
|
||||
let Ok(payment_method_id) =
|
||||
PaymentMethodId::from_str(payment_method)
|
||||
{
|
||||
id
|
||||
} else {
|
||||
else {
|
||||
return Err(ApiError::InvalidInput(
|
||||
"Invalid payment method id".to_string(),
|
||||
));
|
||||
@@ -743,9 +741,7 @@ pub async fn edit_payment_method(
|
||||
|
||||
let (id,) = info.into_inner();
|
||||
|
||||
let payment_method_id = if let Ok(id) = PaymentMethodId::from_str(&id) {
|
||||
id
|
||||
} else {
|
||||
let Ok(payment_method_id) = PaymentMethodId::from_str(&id) else {
|
||||
return Err(ApiError::NotFound);
|
||||
};
|
||||
|
||||
@@ -766,10 +762,7 @@ pub async fn edit_payment_method(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if payment_method
|
||||
.customer
|
||||
.map(|x| x.id() == customer)
|
||||
.unwrap_or(false)
|
||||
if payment_method.customer.is_some_and(|x| x.id() == customer)
|
||||
|| user.role.is_admin()
|
||||
{
|
||||
stripe::Customer::update(
|
||||
@@ -812,9 +805,7 @@ pub async fn remove_payment_method(
|
||||
|
||||
let (id,) = info.into_inner();
|
||||
|
||||
let payment_method_id = if let Ok(id) = PaymentMethodId::from_str(&id) {
|
||||
id
|
||||
} else {
|
||||
let Ok(payment_method_id) = PaymentMethodId::from_str(&id) else {
|
||||
return Err(ApiError::NotFound);
|
||||
};
|
||||
|
||||
@@ -864,10 +855,7 @@ pub async fn remove_payment_method(
|
||||
}
|
||||
}
|
||||
|
||||
if payment_method
|
||||
.customer
|
||||
.map(|x| x.id() == customer)
|
||||
.unwrap_or(false)
|
||||
if payment_method.customer.is_some_and(|x| x.id() == customer)
|
||||
|| user.role.is_admin()
|
||||
{
|
||||
stripe::PaymentMethod::detach(&stripe_client, &payment_method_id)
|
||||
@@ -1437,8 +1425,6 @@ pub async fn stripe_webhook(
|
||||
pub user_subscription_item:
|
||||
Option<user_subscription_item::DBUserSubscription>,
|
||||
pub payment_metadata: Option<PaymentRequestMetadata>,
|
||||
#[allow(dead_code)]
|
||||
pub charge_type: ChargeType,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -1453,24 +1439,20 @@ pub async fn stripe_webhook(
|
||||
transaction: &mut Transaction<'_, Postgres>,
|
||||
) -> Result<PaymentIntentMetadata, ApiError> {
|
||||
'metadata: {
|
||||
let user_id = if let Some(user_id) = metadata
|
||||
let Some(user_id) = metadata
|
||||
.get("modrinth_user_id")
|
||||
.and_then(|x| parse_base62(x).ok())
|
||||
.map(|x| crate::database::models::ids::DBUserId(x as i64))
|
||||
{
|
||||
user_id
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let user = if let Some(user) =
|
||||
let Some(user) =
|
||||
crate::database::models::user_item::DBUser::get_id(
|
||||
user_id, pool, redis,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
user
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
@@ -1478,22 +1460,20 @@ pub async fn stripe_webhook(
|
||||
.get("modrinth_payment_metadata")
|
||||
.and_then(|x| serde_json::from_str(x).ok());
|
||||
|
||||
let charge_id = if let Some(charge_id) = metadata
|
||||
let Some(charge_id) = metadata
|
||||
.get("modrinth_charge_id")
|
||||
.and_then(|x| parse_base62(x).ok())
|
||||
.map(|x| crate::database::models::ids::DBChargeId(x as i64))
|
||||
{
|
||||
charge_id
|
||||
} else {
|
||||
.map(|x| {
|
||||
crate::database::models::ids::DBChargeId(x as i64)
|
||||
})
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let charge_type = if let Some(charge_type) = metadata
|
||||
let Some(charge_type) = metadata
|
||||
.get("modrinth_charge_type")
|
||||
.map(|x| ChargeType::from_string(x))
|
||||
{
|
||||
charge_type
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
@@ -1505,21 +1485,19 @@ pub async fn stripe_webhook(
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let price = if let Some(price) =
|
||||
product_item::DBProductPrice::get(charge.price_id, pool)
|
||||
.await?
|
||||
{
|
||||
price
|
||||
} else {
|
||||
let Some(price) = product_item::DBProductPrice::get(
|
||||
charge.price_id,
|
||||
pool,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let product = if let Some(product) =
|
||||
let Some(product) =
|
||||
product_item::DBProduct::get(price.product_id, pool)
|
||||
.await?
|
||||
{
|
||||
product
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
@@ -1530,15 +1508,13 @@ pub async fn stripe_webhook(
|
||||
charge.upsert(transaction).await?;
|
||||
|
||||
if let Some(subscription_id) = charge.subscription_id {
|
||||
let mut subscription = if let Some(subscription) =
|
||||
let Some(mut subscription) =
|
||||
user_subscription_item::DBUserSubscription::get(
|
||||
subscription_id,
|
||||
pool,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
subscription
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
@@ -1567,58 +1543,49 @@ pub async fn stripe_webhook(
|
||||
(charge, price, product, None)
|
||||
}
|
||||
} else {
|
||||
let price_id = if let Some(price_id) = metadata
|
||||
let Some(price_id) = metadata
|
||||
.get("modrinth_price_id")
|
||||
.and_then(|x| parse_base62(x).ok())
|
||||
.map(|x| {
|
||||
crate::database::models::ids::DBProductPriceId(
|
||||
x as i64,
|
||||
)
|
||||
}) {
|
||||
price_id
|
||||
} else {
|
||||
})
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let price = if let Some(price) =
|
||||
let Some(price) =
|
||||
product_item::DBProductPrice::get(price_id, pool)
|
||||
.await?
|
||||
{
|
||||
price
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let product = if let Some(product) =
|
||||
let Some(product) =
|
||||
product_item::DBProduct::get(price.product_id, pool)
|
||||
.await?
|
||||
{
|
||||
product
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
let subscription = match &price.prices {
|
||||
Price::OneTime { .. } => None,
|
||||
Price::Recurring { intervals } => {
|
||||
let interval = if let Some(interval) = metadata
|
||||
let Some(interval) = metadata
|
||||
.get("modrinth_subscription_interval")
|
||||
.map(|x| PriceDuration::from_string(x))
|
||||
{
|
||||
interval
|
||||
} else {
|
||||
else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
if intervals.get(&interval).is_some() {
|
||||
let subscription_id = if let Some(subscription_id) = metadata
|
||||
let Some(subscription_id) = metadata
|
||||
.get("modrinth_subscription_id")
|
||||
.and_then(|x| parse_base62(x).ok())
|
||||
.map(|x| {
|
||||
crate::database::models::ids::DBUserSubscriptionId(x as i64)
|
||||
}) {
|
||||
subscription_id
|
||||
} else {
|
||||
}) else {
|
||||
break 'metadata;
|
||||
};
|
||||
|
||||
@@ -1687,7 +1654,6 @@ pub async fn stripe_webhook(
|
||||
charge_item: charge,
|
||||
user_subscription_item: subscription,
|
||||
payment_metadata,
|
||||
charge_type,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2049,10 +2015,9 @@ pub async fn stripe_webhook(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !customer
|
||||
if customer
|
||||
.invoice_settings
|
||||
.map(|x| x.default_payment_method.is_some())
|
||||
.unwrap_or(false)
|
||||
.is_none_or(|x| x.default_payment_method.is_none())
|
||||
{
|
||||
stripe::Customer::update(
|
||||
&stripe_client,
|
||||
@@ -2187,12 +2152,10 @@ pub async fn index_subscriptions(pool: PgPool, redis: RedisPool) {
|
||||
.await?;
|
||||
|
||||
for charge in all_charges {
|
||||
let subscription = if let Some(subscription) = all_subscriptions
|
||||
let Some(subscription) = all_subscriptions
|
||||
.iter_mut()
|
||||
.find(|x| Some(x.id) == charge.subscription_id)
|
||||
{
|
||||
subscription
|
||||
} else {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -2200,29 +2163,23 @@ pub async fn index_subscriptions(pool: PgPool, redis: RedisPool) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let product_price = if let Some(product_price) = subscription_prices
|
||||
let Some(product_price) = subscription_prices
|
||||
.iter()
|
||||
.find(|x| x.id == subscription.price_id)
|
||||
{
|
||||
product_price
|
||||
} else {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let product = if let Some(product) = subscription_products
|
||||
let Some(product) = subscription_products
|
||||
.iter()
|
||||
.find(|x| x.id == product_price.product_id)
|
||||
{
|
||||
product
|
||||
} else {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let user = if let Some(user) =
|
||||
let Some(user) =
|
||||
users.iter().find(|x| x.id == subscription.user_id)
|
||||
{
|
||||
user
|
||||
} else {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -2350,19 +2307,14 @@ pub async fn index_billing(
|
||||
.await?;
|
||||
|
||||
for mut charge in charges_to_do {
|
||||
let product_price = if let Some(price) =
|
||||
let Some(product_price) =
|
||||
prices.iter().find(|x| x.id == charge.price_id)
|
||||
{
|
||||
price
|
||||
} else {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let user = if let Some(user) =
|
||||
users.iter().find(|x| x.id == charge.user_id)
|
||||
{
|
||||
user
|
||||
} else {
|
||||
let Some(user) = users.iter().find(|x| x.id == charge.user_id)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -2399,17 +2351,14 @@ pub async fn index_billing(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let currency = match Currency::from_str(
|
||||
let Ok(currency) = Currency::from_str(
|
||||
&product_price.currency_code.to_lowercase(),
|
||||
) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
warn!(
|
||||
"Could not find currency for {}",
|
||||
product_price.currency_code
|
||||
);
|
||||
continue;
|
||||
}
|
||||
) else {
|
||||
warn!(
|
||||
"Could not find currency for {}",
|
||||
product_price.currency_code
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut intent =
|
||||
|
||||
@@ -1249,7 +1249,7 @@ pub async fn delete_auth_provider(
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if !user.auth_providers.map(|x| x.len() > 1).unwrap_or(false)
|
||||
if user.auth_providers.is_none_or(|x| x.len() <= 1)
|
||||
&& !user.has_password.unwrap_or(false)
|
||||
{
|
||||
return Err(ApiError::InvalidInput(
|
||||
|
||||
@@ -217,8 +217,7 @@ pub async fn ws_init(
|
||||
if status
|
||||
.profile_name
|
||||
.as_ref()
|
||||
.map(|x| x.len() > 64)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x.len() > 64)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ fn find_file<'a>(
|
||||
// Minecraft mods are not going to be both a mod and a modpack, so this minecraft-specific handling is fine
|
||||
// As there can be multiple project types, returns the first allowable match
|
||||
let mut fileexts = vec![];
|
||||
for project_type in version.project_types.iter() {
|
||||
for project_type in &version.project_types {
|
||||
match project_type.as_str() {
|
||||
"mod" => fileexts.push("jar"),
|
||||
"modpack" => fileexts.push("mrpack"),
|
||||
@@ -381,8 +381,10 @@ pub async fn version_file_sha1(
|
||||
|
||||
Ok(find_file(&project_id, &vnum, &version, &file)
|
||||
.and_then(|file| file.hashes.get("sha1"))
|
||||
.map(|hash_str| HttpResponse::Ok().body(hash_str.clone()))
|
||||
.unwrap_or_else(|| HttpResponse::NotFound().body("")))
|
||||
.map_or_else(
|
||||
|| HttpResponse::NotFound().body(""),
|
||||
|hash_str| HttpResponse::Ok().body(hash_str.clone()),
|
||||
))
|
||||
}
|
||||
|
||||
#[get("maven/modrinth/{id}/{versionnum}/{file}.sha512")]
|
||||
@@ -426,6 +428,8 @@ pub async fn version_file_sha512(
|
||||
|
||||
Ok(find_file(&project_id, &vnum, &version, &file)
|
||||
.and_then(|file| file.hashes.get("sha512"))
|
||||
.map(|hash_str| HttpResponse::Ok().body(hash_str.clone()))
|
||||
.unwrap_or_else(|| HttpResponse::NotFound().body("")))
|
||||
.map_or_else(
|
||||
|| HttpResponse::NotFound().body(""),
|
||||
|hash_str| HttpResponse::Ok().body(hash_str.clone()),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ pub async fn project_search(
|
||||
val
|
||||
)
|
||||
} else {
|
||||
facet.to_string()
|
||||
facet
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
||||
@@ -283,28 +283,23 @@ async fn get_example_version_fields(
|
||||
pool: Data<PgPool>,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Option<Vec<VersionField>>, CreateError> {
|
||||
let project_id = match project_id {
|
||||
Some(project_id) => project_id,
|
||||
None => return Ok(None),
|
||||
let Some(project_id) = project_id else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let vid = match project_item::DBProject::get_id(
|
||||
project_id.into(),
|
||||
&**pool,
|
||||
redis,
|
||||
)
|
||||
.await?
|
||||
.and_then(|p| p.versions.first().cloned())
|
||||
{
|
||||
Some(vid) => vid,
|
||||
None => return Ok(None),
|
||||
let Some(vid) =
|
||||
project_item::DBProject::get_id(project_id.into(), &**pool, redis)
|
||||
.await?
|
||||
.and_then(|p| p.versions.first().copied())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let example_version =
|
||||
match version_item::DBVersion::get(vid, &**pool, redis).await? {
|
||||
Some(version) => version,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let Some(example_version) =
|
||||
version_item::DBVersion::get(vid, &**pool, redis).await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(example_version.version_fields))
|
||||
}
|
||||
|
||||
|
||||
@@ -256,13 +256,11 @@ pub async fn organization_get(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| {
|
||||
y == x.user_id
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -345,13 +343,11 @@ pub async fn organizations_get(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| {
|
||||
y == x.user_id
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -635,7 +631,7 @@ pub async fn organization_delete(
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
for organization_project_team in organization_project_teams.iter() {
|
||||
for organization_project_team in &organization_project_teams {
|
||||
let new_id = crate::database::models::ids::generate_team_member_id(
|
||||
&mut transaction,
|
||||
)
|
||||
|
||||
@@ -830,14 +830,13 @@ async fn get_user_balance(
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
let (withdrawn, fees) = withdrawn
|
||||
.map(|x| {
|
||||
let (withdrawn, fees) =
|
||||
withdrawn.map_or((Decimal::ZERO, Decimal::ZERO), |x| {
|
||||
(
|
||||
x.amount.unwrap_or(Decimal::ZERO),
|
||||
x.fee.unwrap_or(Decimal::ZERO),
|
||||
)
|
||||
})
|
||||
.unwrap_or((Decimal::ZERO, Decimal::ZERO));
|
||||
});
|
||||
|
||||
Ok(UserBalance {
|
||||
available: available.round_dp(16)
|
||||
|
||||
@@ -360,15 +360,14 @@ async fn project_create_inner(
|
||||
// The first multipart field must be named "data" and contain a
|
||||
// JSON `ProjectCreateData` object.
|
||||
|
||||
let mut field = payload
|
||||
.next()
|
||||
.await
|
||||
.map(|m| m.map_err(CreateError::MultipartError))
|
||||
.unwrap_or_else(|| {
|
||||
let mut field = payload.next().await.map_or_else(
|
||||
|| {
|
||||
Err(CreateError::MissingValueError(String::from(
|
||||
"No `data` field in multipart upload",
|
||||
)))
|
||||
})?;
|
||||
},
|
||||
|m| m.map_err(CreateError::MultipartError),
|
||||
)?;
|
||||
|
||||
let name = field.name().ok_or_else(|| {
|
||||
CreateError::MissingValueError(String::from("Missing content name"))
|
||||
@@ -550,8 +549,8 @@ async fn project_create_inner(
|
||||
)));
|
||||
};
|
||||
// `index` is always valid for these lists
|
||||
let created_version = versions.get_mut(index).unwrap();
|
||||
let version_data = project_create_data.initial_versions.get(index).unwrap();
|
||||
let created_version = &mut versions[index];
|
||||
let version_data = &project_create_data.initial_versions[index];
|
||||
// TODO: maybe redundant is this calculation done elsewhere?
|
||||
|
||||
let existing_file_names = created_version
|
||||
@@ -670,10 +669,9 @@ async fn project_create_inner(
|
||||
&team_member,
|
||||
);
|
||||
|
||||
if !perms
|
||||
.map(|x| x.contains(OrganizationPermissions::ADD_PROJECT))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if !perms.is_some_and(|x| {
|
||||
x.contains(OrganizationPermissions::ADD_PROJECT)
|
||||
}) {
|
||||
return Err(CreateError::CustomAuthenticationError(
|
||||
"You do not have the permissions to create projects in this organization!"
|
||||
.to_string(),
|
||||
|
||||
@@ -448,7 +448,7 @@ pub async fn project_edit(
|
||||
}
|
||||
}
|
||||
|
||||
if team_member.map(|x| !x.accepted).unwrap_or(true) {
|
||||
if team_member.is_none_or(|x| !x.accepted) {
|
||||
let notified_members = sqlx::query!(
|
||||
"
|
||||
SELECT tm.user_id id
|
||||
@@ -2397,13 +2397,11 @@ pub async fn project_get_organization(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| {
|
||||
y == x.user_id
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
|
||||
@@ -148,16 +148,15 @@ pub async fn loader_fields_list(
|
||||
))
|
||||
})?;
|
||||
|
||||
let loader_field_enum_id = match loader_field.field_type {
|
||||
LoaderFieldType::Enum(enum_id)
|
||||
| LoaderFieldType::ArrayEnum(enum_id) => enum_id,
|
||||
_ => {
|
||||
return Err(ApiError::InvalidInput(format!(
|
||||
"'{}' is not an enumerable field, but an '{}' field.",
|
||||
query.loader_field,
|
||||
loader_field.field_type.to_str()
|
||||
)));
|
||||
}
|
||||
let (LoaderFieldType::Enum(loader_field_enum_id)
|
||||
| LoaderFieldType::ArrayEnum(loader_field_enum_id)) =
|
||||
loader_field.field_type
|
||||
else {
|
||||
return Err(ApiError::InvalidInput(format!(
|
||||
"'{}' is not an enumerable field, but an '{}' field.",
|
||||
query.loader_field,
|
||||
loader_field.field_type.to_str()
|
||||
)));
|
||||
};
|
||||
|
||||
let results: Vec<_> = if let Some(filters) = query.filters {
|
||||
|
||||
@@ -101,13 +101,11 @@ pub async fn team_members_get_project(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| {
|
||||
y == x.user_id
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -176,13 +174,11 @@ pub async fn team_members_get_organization(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| {
|
||||
y == x.user_id
|
||||
})
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -242,11 +238,11 @@ pub async fn team_members_get(
|
||||
.filter(|x| {
|
||||
logged_in
|
||||
|| x.accepted
|
||||
|| user_id
|
||||
.map(|y: crate::database::models::DBUserId| y == x.user_id)
|
||||
.unwrap_or(false)
|
||||
|| user_id.is_some_and(
|
||||
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||
)
|
||||
})
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -319,7 +315,7 @@ pub async fn teams_get(
|
||||
let team_members = members
|
||||
.into_iter()
|
||||
.filter(|x| logged_in || x.accepted)
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -592,8 +588,7 @@ pub async fn add_team_member(
|
||||
};
|
||||
if new_user_organization_team_member
|
||||
.as_ref()
|
||||
.map(|tm| tm.is_owner)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|tm| tm.is_owner)
|
||||
&& new_member.permissions != ProjectPermissions::all()
|
||||
{
|
||||
return Err(ApiError::InvalidInput(
|
||||
@@ -748,12 +743,10 @@ pub async fn edit_team_member(
|
||||
|
||||
if organization_team_member
|
||||
.as_ref()
|
||||
.map(|x| x.is_owner)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x.is_owner)
|
||||
&& edit_member
|
||||
.permissions
|
||||
.map(|x| x != ProjectPermissions::all())
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x != ProjectPermissions::all())
|
||||
{
|
||||
return Err(ApiError::CustomAuthentication(
|
||||
"You cannot override the project permissions of the organization owner!"
|
||||
@@ -1011,7 +1004,7 @@ pub async fn transfer_ownership(
|
||||
.collect();
|
||||
|
||||
// If the owner of the organization is a member of the project, remove them
|
||||
for team_id in team_ids.iter() {
|
||||
for team_id in &team_ids {
|
||||
DBTeamMember::delete(
|
||||
*team_id,
|
||||
new_owner.user_id.into(),
|
||||
|
||||
@@ -119,7 +119,7 @@ pub async fn filter_authorized_threads(
|
||||
let project_thread_ids = check_threads
|
||||
.iter()
|
||||
.filter(|x| x.type_ == ThreadType::Project)
|
||||
.flat_map(|x| x.project_id.map(|x| x.0))
|
||||
.filter_map(|x| x.project_id.map(|x| x.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !project_thread_ids.is_empty() {
|
||||
@@ -148,13 +148,12 @@ pub async fn filter_authorized_threads(
|
||||
.await?;
|
||||
}
|
||||
|
||||
let org_project_thread_ids = check_threads
|
||||
let mut org_project_thread_ids = check_threads
|
||||
.iter()
|
||||
.filter(|x| x.type_ == ThreadType::Project)
|
||||
.flat_map(|x| x.project_id.map(|x| x.0))
|
||||
.collect::<Vec<_>>();
|
||||
.filter_map(|x| x.project_id.map(|x| x.0));
|
||||
|
||||
if !org_project_thread_ids.is_empty() {
|
||||
if org_project_thread_ids.next().is_some() {
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT m.id FROM mods m
|
||||
@@ -184,7 +183,7 @@ pub async fn filter_authorized_threads(
|
||||
let report_thread_ids = check_threads
|
||||
.iter()
|
||||
.filter(|x| x.type_ == ThreadType::Report)
|
||||
.flat_map(|x| x.report_id.map(|x| x.0))
|
||||
.filter_map(|x| x.report_id.map(|x| x.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !report_thread_ids.is_empty() {
|
||||
|
||||
@@ -207,7 +207,7 @@ pub async fn user_get(
|
||||
.ok();
|
||||
|
||||
let response: crate::models::users::User =
|
||||
if auth_user.map(|x| x.role.is_admin()).unwrap_or(false) {
|
||||
if auth_user.is_some_and(|x| x.role.is_admin()) {
|
||||
crate::models::users::User::from_full(data)
|
||||
} else {
|
||||
data.into()
|
||||
@@ -242,9 +242,8 @@ pub async fn collections_list(
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
let user_id: UserId = id.into();
|
||||
|
||||
let can_view_private = user
|
||||
.map(|y| y.role.is_mod() || y.id == user_id)
|
||||
.unwrap_or(false);
|
||||
let can_view_private =
|
||||
user.is_some_and(|y| y.role.is_mod() || y.id == user_id);
|
||||
|
||||
let project_data = DBUser::get_collections(id, &**pool).await?;
|
||||
|
||||
@@ -334,7 +333,7 @@ pub async fn orgs_list(
|
||||
let team_members: Vec<_> = members_data
|
||||
.into_iter()
|
||||
.filter(|x| logged_in || x.accepted || id == x.user_id)
|
||||
.flat_map(|data| {
|
||||
.filter_map(|data| {
|
||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||
crate::models::teams::TeamMember::from(
|
||||
data,
|
||||
@@ -412,8 +411,7 @@ pub async fn user_edit(
|
||||
|
||||
if existing_user_id_option
|
||||
.map(|x| UserId::from(x.id))
|
||||
.map(|id| id == user.id)
|
||||
.unwrap_or(true)
|
||||
.is_none_or(|id| id == user.id)
|
||||
{
|
||||
sqlx::query!(
|
||||
"
|
||||
|
||||
@@ -606,13 +606,10 @@ async fn upload_file_to_version_inner(
|
||||
|
||||
let result = models::DBVersion::get(version_id, &**client, &redis).await?;
|
||||
|
||||
let version = match result {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(CreateError::InvalidInput(
|
||||
"An invalid version id was supplied".to_string(),
|
||||
));
|
||||
}
|
||||
let Some(version) = result else {
|
||||
return Err(CreateError::InvalidInput(
|
||||
"An invalid version id was supplied".to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let all_loaders =
|
||||
@@ -1065,7 +1062,7 @@ pub fn try_create_version_fields(
|
||||
.filter(|lf| !lf.optional)
|
||||
.map(|lf| lf.field.clone())
|
||||
.collect::<HashSet<_>>();
|
||||
for (key, value) in submitted_fields.iter() {
|
||||
for (key, value) in submitted_fields {
|
||||
let loader_field = loader_fields
|
||||
.iter()
|
||||
.find(|lf| &lf.field == key)
|
||||
|
||||
@@ -794,8 +794,7 @@ pub async fn version_list(
|
||||
.filter(|version| {
|
||||
filters
|
||||
.featured
|
||||
.map(|featured| featured == version.inner.featured)
|
||||
.unwrap_or(true)
|
||||
.is_none_or(|featured| featured == version.inner.featured)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
@@ -830,22 +829,20 @@ pub async fn version_list(
|
||||
}
|
||||
|
||||
joined_filters.into_iter().for_each(|filter| {
|
||||
versions
|
||||
.iter()
|
||||
.find(|version| {
|
||||
// TODO: This is the bandaid fix for detecting auto-featured versions.
|
||||
let game_versions = version
|
||||
.version_fields
|
||||
.iter()
|
||||
.find(|vf| vf.field_name == "game_versions")
|
||||
.map(|vf| vf.value.clone())
|
||||
.map(|v| v.as_strings())
|
||||
.unwrap_or_default();
|
||||
game_versions.contains(&filter.0.version)
|
||||
&& version.loaders.contains(&filter.1.loader)
|
||||
})
|
||||
.map(|version| response.push(version.clone()))
|
||||
.unwrap_or(());
|
||||
if let Some(version) = versions.iter().find(|version| {
|
||||
// TODO: This is the bandaid fix for detecting auto-featured versions.
|
||||
let game_versions = version
|
||||
.version_fields
|
||||
.iter()
|
||||
.find(|vf| vf.field_name == "game_versions")
|
||||
.map(|vf| vf.value.clone())
|
||||
.map(|v| v.as_strings())
|
||||
.unwrap_or_default();
|
||||
game_versions.contains(&filter.0.version)
|
||||
&& version.loaders.contains(&filter.1.loader)
|
||||
}) {
|
||||
response.push(version.clone());
|
||||
}
|
||||
});
|
||||
|
||||
if response.is_empty() {
|
||||
|
||||
@@ -522,7 +522,7 @@ async fn index_versions(
|
||||
// Convert to partial versions
|
||||
let mut res_versions: HashMap<DBProjectId, Vec<PartialVersion>> =
|
||||
HashMap::new();
|
||||
for (project_id, version_ids) in versions.iter() {
|
||||
for (project_id, version_ids) in &versions {
|
||||
for version_id in version_ids {
|
||||
// Extract version-specific data fetched
|
||||
// We use 'remove' as every version is only in the map once
|
||||
|
||||
@@ -13,7 +13,6 @@ pub struct MultipartSegment {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum MultipartSegmentData {
|
||||
Text(String),
|
||||
Binary(Vec<u8>),
|
||||
|
||||
@@ -98,13 +98,12 @@ pub async fn upload_image_optimized(
|
||||
|
||||
let url = format!("{}/{}", cdn_url, upload_data.file_name);
|
||||
Ok(UploadImageResult {
|
||||
url: processed_upload_data
|
||||
.clone()
|
||||
.map(|x| format!("{}/{}", cdn_url, x.file_name))
|
||||
.unwrap_or_else(|| url.clone()),
|
||||
url: processed_upload_data.clone().map_or_else(
|
||||
|| url.clone(),
|
||||
|x| format!("{}/{}", cdn_url, x.file_name),
|
||||
),
|
||||
url_path: processed_upload_data
|
||||
.map(|x| x.file_name)
|
||||
.unwrap_or_else(|| upload_data.file_name.clone()),
|
||||
.map_or_else(|| upload_data.file_name.clone(), |x| x.file_name),
|
||||
|
||||
raw_url: url,
|
||||
raw_url_path: upload_data.file_name,
|
||||
@@ -119,7 +118,7 @@ fn process_image(
|
||||
min_aspect_ratio: Option<f32>,
|
||||
) -> Result<(bytes::Bytes, String), ImageError> {
|
||||
if content_type.to_lowercase() == "image/gif" {
|
||||
return Ok((image_bytes.clone(), "gif".to_string()));
|
||||
return Ok((image_bytes, "gif".to_string()));
|
||||
}
|
||||
|
||||
let mut img = image::load_from_memory(&image_bytes)?;
|
||||
|
||||
@@ -56,18 +56,15 @@ impl AsyncRateLimiter {
|
||||
}
|
||||
|
||||
pub async fn check_rate_limit(&self, key: &str) -> RateLimitDecision {
|
||||
let mut conn = match self.redis_pool.connect().await {
|
||||
Ok(conn) => conn,
|
||||
Err(_) => {
|
||||
// If Redis is unavailable, allow the request but with reduced limit
|
||||
return RateLimitDecision {
|
||||
allowed: true,
|
||||
limit: self.params.burst_size,
|
||||
remaining: 1,
|
||||
reset_after_ms: 60_000, // 1 minute
|
||||
retry_after_ms: None,
|
||||
};
|
||||
}
|
||||
let Ok(mut conn) = self.redis_pool.connect().await else {
|
||||
// If Redis is unavailable, allow the request but with reduced limit
|
||||
return RateLimitDecision {
|
||||
allowed: true,
|
||||
limit: self.params.burst_size,
|
||||
remaining: 1,
|
||||
reset_after_ms: 60_000, // 1 minute
|
||||
retry_after_ms: None,
|
||||
};
|
||||
};
|
||||
|
||||
// Get current time in nanoseconds since UNIX epoch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::sync::LazyLock;
|
||||
use std::{fmt::Write, sync::LazyLock};
|
||||
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
@@ -44,15 +44,17 @@ pub fn validation_errors_to_string(
|
||||
ValidationErrorsKind::Field(errors) => {
|
||||
if let Some(error) = errors.first() {
|
||||
if let Some(adder) = adder {
|
||||
output.push_str(&format!(
|
||||
"Field {} {} failed validation with error: {}",
|
||||
field, adder, error.code
|
||||
));
|
||||
write!(
|
||||
&mut output,
|
||||
"Field {field} {adder} failed validation with error: {}",
|
||||
error.code
|
||||
).unwrap();
|
||||
} else {
|
||||
output.push_str(&format!(
|
||||
"Field {} failed validation with error: {}",
|
||||
field, error.code
|
||||
));
|
||||
write!(
|
||||
&mut output,
|
||||
"Field {field} failed validation with error: {}",
|
||||
error.code
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ pub enum SupportedGameVersions {
|
||||
All,
|
||||
PastDate(DateTime<Utc>),
|
||||
Range(DateTime<Utc>, DateTime<Utc>),
|
||||
#[allow(dead_code)]
|
||||
Custom(Vec<MinecraftGameVersion>),
|
||||
}
|
||||
|
||||
@@ -232,8 +231,7 @@ fn game_version_supported(
|
||||
all_game_versions
|
||||
.iter()
|
||||
.find(|y| y.version == x.version)
|
||||
.map(|x| x.created > date)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x.created > date)
|
||||
})
|
||||
}
|
||||
SupportedGameVersions::Range(before, after) => {
|
||||
@@ -241,8 +239,7 @@ fn game_version_supported(
|
||||
all_game_versions
|
||||
.iter()
|
||||
.find(|y| y.version == x.version)
|
||||
.map(|x| x.created > before && x.created < after)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x.created > before && x.created < after)
|
||||
})
|
||||
}
|
||||
SupportedGameVersions::Custom(versions) => {
|
||||
|
||||
@@ -28,14 +28,11 @@ impl super::Validator for ModpackValidator {
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
let pack: PackFormat = {
|
||||
let mut file =
|
||||
if let Ok(file) = archive.by_name("modrinth.index.json") {
|
||||
file
|
||||
} else {
|
||||
return Ok(ValidationResult::Warning(
|
||||
"Pack manifest is missing.",
|
||||
));
|
||||
};
|
||||
let Ok(mut file) = archive.by_name("modrinth.index.json") else {
|
||||
return Ok(ValidationResult::Warning(
|
||||
"Pack manifest is missing.",
|
||||
));
|
||||
};
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
@@ -109,7 +106,7 @@ impl super::Validator for ModpackValidator {
|
||||
|| x.starts_with("overrides/shaderpacks")
|
||||
|| x.starts_with("client-overrides/shaderpacks"))
|
||||
})
|
||||
.flat_map(|x| x.rsplit('/').next().map(|x| x.to_string()))
|
||||
.filter_map(|x| x.rsplit('/').next().map(|x| x.to_string()))
|
||||
.collect::<Vec<String>>(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user