Fix clippy errors + lint, use turbo CI

This commit is contained in:
Jai A
2024-10-18 16:07:35 -07:00
parent 663ab83b08
commit 8dd955563e
186 changed files with 10615 additions and 6433 deletions

View File

@@ -20,11 +20,17 @@ pub enum MultipartSegmentData {
}
pub trait AppendsMultipart {
fn set_multipart(self, data: impl IntoIterator<Item = MultipartSegment>) -> Self;
fn set_multipart(
self,
data: impl IntoIterator<Item = MultipartSegment>,
) -> Self;
}
impl AppendsMultipart for TestRequest {
fn set_multipart(self, data: impl IntoIterator<Item = MultipartSegment>) -> Self {
fn set_multipart(
self,
data: impl IntoIterator<Item = MultipartSegment>,
) -> Self {
let (boundary, payload) = generate_multipart(data);
self.append_header((
"Content-Type",
@@ -34,7 +40,9 @@ impl AppendsMultipart for TestRequest {
}
}
pub fn generate_multipart(data: impl IntoIterator<Item = MultipartSegment>) -> (String, Bytes) {
pub fn generate_multipart(
data: impl IntoIterator<Item = MultipartSegment>,
) -> (String, Bytes) {
let mut boundary: String = String::from("----WebKitFormBoundary");
boundary.push_str(&rand::random::<u64>().to_string());
boundary.push_str(&rand::random::<u64>().to_string());
@@ -54,7 +62,8 @@ pub fn generate_multipart(data: impl IntoIterator<Item = MultipartSegment>) -> (
if let Some(filename) = &segment.filename {
payload.extend_from_slice(
format!("; filename=\"{filename}\"", filename = filename).as_bytes(),
format!("; filename=\"{filename}\"", filename = filename)
.as_bytes(),
);
}
if let Some(content_type) = &segment.content_type {
@@ -78,7 +87,9 @@ pub fn generate_multipart(data: impl IntoIterator<Item = MultipartSegment>) -> (
}
payload.extend_from_slice(b"\r\n");
}
payload.extend_from_slice(format!("--{boundary}--\r\n", boundary = boundary).as_bytes());
payload.extend_from_slice(
format!("--{boundary}--\r\n", boundary = boundary).as_bytes(),
);
(boundary, Bytes::from(payload))
}

View File

@@ -2,13 +2,18 @@
macro_rules! bitflags_serde_impl {
($type:ident, $int_type:ident) => {
impl serde::Serialize for $type {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_i64(self.bits() as i64)
}
}
impl<'de> serde::Deserialize<'de> for $type {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
let v: i64 = Deserialize::deserialize(deserializer)?;
Ok($type::from_bits_truncate(v as $int_type))

View File

@@ -4,7 +4,10 @@ use actix_web::HttpRequest;
use serde::Deserialize;
use serde_json::json;
pub async fn check_turnstile_captcha(req: &HttpRequest, challenge: &str) -> Result<bool, ApiError> {
pub async fn check_turnstile_captcha(
req: &HttpRequest,
challenge: &str,
) -> Result<bool, ApiError> {
let conn_info = req.connection_info().clone();
let ip_addr = if parse_var("CLOUDFLARE_INTEGRATION").unwrap_or(false) {
if let Some(header) = req.headers().get("CF-Connecting-IP") {

View File

@@ -2,8 +2,9 @@ use actix_web::guard::GuardContext;
pub const ADMIN_KEY_HEADER: &str = "Modrinth-Admin";
pub fn admin_key_guard(ctx: &GuardContext) -> bool {
let admin_key = std::env::var("LABRINTH_ADMIN_KEY")
.expect("No admin key provided, this should have been caught by check_env_vars");
let admin_key = std::env::var("LABRINTH_ADMIN_KEY").expect(
"No admin key provided, this should have been caught by check_env_vars",
);
ctx.head()
.headers()
.get(ADMIN_KEY_HEADER)

View File

@@ -6,7 +6,10 @@ use crate::models::images::ImageContext;
use crate::routes::ApiError;
use color_thief::ColorFormat;
use image::imageops::FilterType;
use image::{DynamicImage, EncodableLayout, GenericImageView, ImageError, ImageOutputFormat};
use image::{
DynamicImage, EncodableLayout, GenericImageView, ImageError,
ImageOutputFormat,
};
use std::io::Cursor;
use webp::Encoder;
@@ -14,10 +17,15 @@ pub fn get_color_from_img(data: &[u8]) -> Result<Option<u32>, ImageError> {
let image = image::load_from_memory(data)?
.resize(256, 256, FilterType::Nearest)
.crop_imm(128, 128, 64, 64);
let color = color_thief::get_palette(image.to_rgb8().as_bytes(), ColorFormat::Rgb, 10, 2)
.ok()
.and_then(|x| x.first().copied())
.map(|x| (x.r as u32) << 16 | (x.g as u32) << 8 | (x.b as u32));
let color = color_thief::get_palette(
image.to_rgb8().as_bytes(),
ColorFormat::Rgb,
10,
2,
)
.ok()
.and_then(|x| x.first().copied())
.map(|x| (x.r as u32) << 16 | (x.g as u32) << 8 | (x.b as u32));
Ok(color)
}
@@ -40,16 +48,23 @@ pub async fn upload_image_optimized(
min_aspect_ratio: Option<f32>,
file_host: &dyn FileHost,
) -> Result<UploadImageResult, ApiError> {
let content_type =
crate::util::ext::get_image_content_type(file_extension).ok_or_else(|| {
ApiError::InvalidInput(format!("Invalid format for image: {}", file_extension))
let content_type = crate::util::ext::get_image_content_type(file_extension)
.ok_or_else(|| {
ApiError::InvalidInput(format!(
"Invalid format for image: {}",
file_extension
))
})?;
let cdn_url = dotenvy::var("CDN_URL")?;
let hash = sha1::Sha1::from(&bytes).hexdigest();
let (processed_image, processed_image_ext) =
process_image(bytes.clone(), content_type, target_width, min_aspect_ratio)?;
let (processed_image, processed_image_ext) = process_image(
bytes.clone(),
content_type,
target_width,
min_aspect_ratio,
)?;
let color = get_color_from_img(&bytes)?;
// Only upload the processed image if it's smaller than the original
@@ -118,7 +133,8 @@ fn process_image(
if let Some(target_width) = target_width {
if img.width() > target_width {
let new_height = (target_width as f32 / aspect_ratio).round() as u32;
let new_height =
(target_width as f32 / aspect_ratio).round() as u32;
img = img.resize(target_width, new_height, FilterType::Lanczos3);
}
}
@@ -126,7 +142,8 @@ fn process_image(
if let Some(min_aspect_ratio) = min_aspect_ratio {
// Crop if necessary
if aspect_ratio < min_aspect_ratio {
let crop_height = (img.width() as f32 / min_aspect_ratio).round() as u32;
let crop_height =
(img.width() as f32 / min_aspect_ratio).round() as u32;
let y_offset = (img.height() - crop_height) / 2;
img = img.crop_imm(0, y_offset, img.width(), crop_height);
}
@@ -181,7 +198,9 @@ pub async fn delete_unused_images(
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
redis: &RedisPool,
) -> Result<(), ApiError> {
let uploaded_images = database::models::Image::get_many_contexted(context, transaction).await?;
let uploaded_images =
database::models::Image::get_many_contexted(context, transaction)
.await?;
for image in uploaded_images {
let mut should_delete = true;

View File

@@ -13,8 +13,12 @@ use actix_web::{
use futures_util::future::LocalBoxFuture;
use futures_util::future::{ready, Ready};
pub type KeyedRateLimiter<K = String, MW = middleware::StateInformationMiddleware> =
Arc<RateLimiter<K, state::keyed::DefaultKeyedStateStore<K>, DefaultClock, MW>>;
pub type KeyedRateLimiter<
K = String,
MW = middleware::StateInformationMiddleware,
> = Arc<
RateLimiter<K, state::keyed::DefaultKeyedStateStore<K>, DefaultClock, MW>,
>;
pub struct RateLimit(pub KeyedRateLimiter);
@@ -58,7 +62,9 @@ where
fn call(&self, req: ServiceRequest) -> Self::Future {
if let Some(key) = req.headers().get("x-ratelimit-key") {
if key.to_str().ok() == dotenvy::var("RATE_LIMIT_IGNORE_KEY").ok().as_deref() {
if key.to_str().ok()
== dotenvy::var("RATE_LIMIT_IGNORE_KEY").ok().as_deref()
{
let res = self.service.call(req);
return Box::pin(async move {
@@ -129,7 +135,8 @@ where
})
}
Err(negative) => {
let wait_time = negative.wait_time_from(DefaultClock::default().now());
let wait_time =
negative.wait_time_from(DefaultClock::default().now());
let mut response = ApiError::RateLimitError(
wait_time.as_millis(),
@@ -140,28 +147,41 @@ where
let headers = response.headers_mut();
headers.insert(
actix_web::http::header::HeaderName::from_str("x-ratelimit-limit").unwrap(),
actix_web::http::header::HeaderName::from_str(
"x-ratelimit-limit",
)
.unwrap(),
negative.quota().burst_size().get().into(),
);
headers.insert(
actix_web::http::header::HeaderName::from_str("x-ratelimit-remaining")
.unwrap(),
actix_web::http::header::HeaderName::from_str(
"x-ratelimit-remaining",
)
.unwrap(),
0.into(),
);
headers.insert(
actix_web::http::header::HeaderName::from_str("x-ratelimit-reset").unwrap(),
actix_web::http::header::HeaderName::from_str(
"x-ratelimit-reset",
)
.unwrap(),
wait_time.as_secs().into(),
);
Box::pin(async { Ok(req.into_response(response.map_into_right_body())) })
Box::pin(async {
Ok(req.into_response(response.map_into_right_body()))
})
}
}
} else {
let response =
ApiError::CustomAuthentication("Unable to obtain user IP address!".to_string())
.error_response();
let response = ApiError::CustomAuthentication(
"Unable to obtain user IP address!".to_string(),
)
.error_response();
Box::pin(async { Ok(req.into_response(response.map_into_right_body())) })
Box::pin(async {
Ok(req.into_response(response.map_into_right_body()))
})
}
}
}

View File

@@ -13,6 +13,6 @@ pub async fn redis_execute<T>(
where
T: redis::FromRedisValue,
{
let res = cmd.query_async::<_, T>(redis).await?;
let res = cmd.query_async::<T>(redis).await?;
Ok(res)
}

View File

@@ -16,7 +16,9 @@ pub async fn read_from_payload(
return Err(ApiError::InvalidInput(String::from(err_msg)));
} else {
bytes.extend_from_slice(&item.map_err(|_| {
ApiError::InvalidInput("Unable to parse bytes in payload sent!".to_string())
ApiError::InvalidInput(
"Unable to parse bytes in payload sent!".to_string(),
)
})?);
}
}

View File

@@ -6,11 +6,15 @@ use validator::{ValidationErrors, ValidationErrorsKind};
use crate::models::pats::Scopes;
lazy_static! {
pub static ref RE_URL_SAFE: Regex = Regex::new(r#"^[a-zA-Z0-9!@$()`.+,_"-]*$"#).unwrap();
pub static ref RE_URL_SAFE: Regex =
Regex::new(r#"^[a-zA-Z0-9!@$()`.+,_"-]*$"#).unwrap();
}
//TODO: In order to ensure readability, only the first error is printed, this may need to be expanded on in the future!
pub fn validation_errors_to_string(errors: ValidationErrors, adder: Option<String>) -> String {
pub fn validation_errors_to_string(
errors: ValidationErrors,
adder: Option<String>,
) -> String {
let mut output = String::new();
let map = errors.into_errors();
@@ -21,7 +25,10 @@ pub fn validation_errors_to_string(errors: ValidationErrors, adder: Option<Strin
if let Some(error) = map.get(field) {
return match error {
ValidationErrorsKind::Struct(errors) => {
validation_errors_to_string(*errors.clone(), Some(format!("of item {field}")))
validation_errors_to_string(
*errors.clone(),
Some(format!("of item {field}")),
)
}
ValidationErrorsKind::List(list) => {
if let Some((index, errors)) = list.iter().next() {
@@ -113,7 +120,9 @@ pub fn validate_url_hashmap_values(
Ok(())
}
pub fn validate_no_restricted_scopes(value: &Scopes) -> Result<(), validator::ValidationError> {
pub fn validate_no_restricted_scopes(
value: &Scopes,
) -> Result<(), validator::ValidationError> {
if value.is_restricted() {
return Err(validator::ValidationError::new(
"Restricted scopes not allowed",

View File

@@ -47,9 +47,12 @@ async fn get_webhook_metadata(
redis: &RedisPool,
emoji: bool,
) -> Result<Option<WebhookMetadata>, ApiError> {
let project =
crate::database::models::project_item::Project::get_id(project_id.into(), pool, redis)
.await?;
let project = crate::database::models::project_item::Project::get_id(
project_id.into(),
pool,
redis,
)
.await?;
if let Some(mut project) = project {
let mut owner = None;
@@ -82,9 +85,12 @@ async fn get_webhook_metadata(
.await?;
if let Some(member) = team.into_iter().find(|x| x.is_owner) {
let user =
crate::database::models::user_item::User::get_id(member.user_id, pool, redis)
.await?;
let user = crate::database::models::user_item::User::get_id(
member.user_id,
pool,
redis,
)
.await?;
if let Some(user) = user {
owner = Some(WebhookAuthor {
@@ -100,13 +106,16 @@ async fn get_webhook_metadata(
}
};
let all_game_versions = MinecraftGameVersion::list(None, None, pool, redis).await?;
let all_game_versions =
MinecraftGameVersion::list(None, None, pool, redis).await?;
let versions = project
.aggregate_version_fields
.clone()
.into_iter()
.find_map(|vf| MinecraftGameVersion::try_from_version_field(&vf).ok())
.find_map(|vf| {
MinecraftGameVersion::try_from_version_field(&vf).ok()
})
.unwrap_or_default();
let formatted_game_versions = get_gv_range(versions, all_game_versions);
@@ -196,7 +205,10 @@ async fn get_webhook_metadata(
_ => 1049805243866681424,
};
format!("<:{loader}:{emoji_id}> {}{x}", x.remove(0).to_uppercase())
format!(
"<:{loader}:{emoji_id}> {}{x}",
x.remove(0).to_uppercase()
)
} else {
format!("{}{x}", x.remove(0).to_uppercase())
}
@@ -323,7 +335,11 @@ pub async fn send_slack_webhook(
}))
.send()
.await
.map_err(|_| ApiError::Discord("Error while sending projects webhook".to_string()))?;
.map_err(|_| {
ApiError::Discord(
"Error while sending projects webhook".to_string(),
)
})?;
}
Ok(())
@@ -436,7 +452,9 @@ pub async fn send_discord_webhook(
.map(|x| DiscordEmbedImage { url: Some(x) }),
footer: Some(DiscordEmbedFooter {
text: format!("{} on Modrinth", project.display_project_type),
icon_url: Some("https://cdn-raw.modrinth.com/modrinth-new.png".to_string()),
icon_url: Some(
"https://cdn-raw.modrinth.com/modrinth-new.png".to_string(),
),
}),
};
@@ -445,14 +463,21 @@ pub async fn send_discord_webhook(
client
.post(&webhook_url)
.json(&DiscordWebhook {
avatar_url: Some("https://cdn.modrinth.com/Modrinth_Dark_Logo.png".to_string()),
avatar_url: Some(
"https://cdn.modrinth.com/Modrinth_Dark_Logo.png"
.to_string(),
),
username: Some("Modrinth Release".to_string()),
embeds: vec![embed],
content: message,
})
.send()
.await
.map_err(|_| ApiError::Discord("Error while sending projects webhook".to_string()))?;
.map_err(|_| {
ApiError::Discord(
"Error while sending projects webhook".to_string(),
)
})?;
}
Ok(())
@@ -496,15 +521,21 @@ fn get_gv_range(
} else {
let interval_base = &intervals[current_interval];
if ((index as i32) - (interval_base[interval_base.len() - 1][1] as i32) == 1
|| (release_index as i32) - (interval_base[interval_base.len() - 1][2] as i32) == 1)
if ((index as i32)
- (interval_base[interval_base.len() - 1][1] as i32)
== 1
|| (release_index as i32)
- (interval_base[interval_base.len() - 1][2] as i32)
== 1)
&& (all_game_versions[interval_base[0][1]].type_ == "release"
|| all_game_versions[index].type_ != "release")
{
if intervals[current_interval].get(1).is_some() {
intervals[current_interval][1] = vec![i, index, release_index];
intervals[current_interval][1] =
vec![i, index, release_index];
} else {
intervals[current_interval].insert(1, vec![i, index, release_index]);
intervals[current_interval]
.insert(1, vec![i, index, release_index]);
}
} else {
current_interval += 1;
@@ -516,7 +547,10 @@ fn get_gv_range(
let mut new_intervals = Vec::new();
for interval in intervals {
if interval.len() == 2 && interval[0][2] != MAX_VALUE && interval[1][2] == MAX_VALUE {
if interval.len() == 2
&& interval[0][2] != MAX_VALUE
&& interval[1][2] == MAX_VALUE
{
let mut last_snapshot: Option<usize> = None;
for j in ((interval[0][1] + 1)..=interval[1][1]).rev() {
@@ -526,12 +560,16 @@ fn get_gv_range(
vec![
game_versions
.iter()
.position(|x| x.version == all_game_versions[j].version)
.position(|x| {
x.version == all_game_versions[j].version
})
.unwrap_or(MAX_VALUE),
j,
all_releases
.iter()
.position(|x| x.version == all_game_versions[j].version)
.position(|x| {
x.version == all_game_versions[j].version
})
.unwrap_or(MAX_VALUE),
],
]);
@@ -543,7 +581,10 @@ fn get_gv_range(
game_versions
.iter()
.position(|x| {
x.version == all_game_versions[last_snapshot].version
x.version
== all_game_versions
[last_snapshot]
.version
})
.unwrap_or(MAX_VALUE),
last_snapshot,
@@ -572,7 +613,8 @@ fn get_gv_range(
if interval.len() == 2 {
output.push(format!(
"{}{}",
&game_versions[interval[0][0]].version, &game_versions[interval[1][0]].version
&game_versions[interval[0][0]].version,
&game_versions[interval[1][0]].version
))
} else {
output.push(game_versions[interval[0][0]].version.clone())