diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee62eb1..26f6a9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,9 @@ jobs: - name: Check Formatting run: cargo fmt --all -- --check + - name: Run Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + # Run tests (Non-chain only, as requested) - name: Run Tests run: cargo test -- --skip chain_ --test-threads=1 --nocapture diff --git a/src/errors.rs b/src/errors.rs index 4c7b5f5..6b5cfaa 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -150,7 +150,7 @@ fn map_db_error(err: DbError) -> (StatusCode, String) { if msg.contains("duplicate key value violates unique constraint") { ( StatusCode::CONFLICT, - format!("The given value is conflicting with existing record"), + "The given value is conflicting with existing record".to_string(), ) } else { ( diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs index b0ab229..effc21e 100644 --- a/src/handlers/auth.rs +++ b/src/handlers/auth.rs @@ -85,13 +85,13 @@ pub async fn verify_login( warn!(error = %e, "verify_login: verify_address error"); } let addr_ok = addr_res.map_err(|_| { - AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized(format!( - "address verification failed" - )))) + AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( + "address verification failed".to_string(), + ))) })?; if !addr_ok { return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( - format!("address verification failed"), + "address verification failed".to_string(), )))); } let sig_res = SignatureService::verify_message(message.as_bytes(), &body.signature, &body.public_key); @@ -99,14 +99,14 @@ pub async fn verify_login( warn!(error = %e, "verify_login: verify_message error"); } let sig_ok = sig_res.map_err(|_| { - AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized(format!( - "message verification failed" - )))) + AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( + "message verification failed".to_string(), + ))) })?; debug!(addr_ok = addr_ok, sig_ok = sig_ok, "verify_login: verification results"); if !sig_ok { return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( - format!("message verification failed"), + "message verification failed".to_string(), )))); } @@ -168,7 +168,7 @@ pub async fn handle_x_oauth( tracing::info!("Quan address from token: {}", quan_address); let (auth_url, verifier) = state.twitter_gateway.generate_auth_url(); - let session_id = format!("{}|{}", quan_address, uuid::Uuid::new_v4().to_string()); + let session_id = format!("{}|{}", quan_address, uuid::Uuid::new_v4()); tracing::info!("Session id in cookies: {}", session_id); @@ -254,9 +254,9 @@ pub async fn handle_x_oauth_callback( tracing::debug!("Do X association..."); let quan_address = { let Some(address) = session_id.split_once('|').map(|(left, _)| left) else { - return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::OAuth(format!( - "Session id malformed", - ))))); + return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::OAuth( + "Session id malformed".to_string(), + )))); }; address.to_string() diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index a9340d4..8997b1c 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -130,7 +130,5 @@ pub fn validate_pagination_query(page: u32, page_size: u32) -> Result<(), AppErr } fn calculate_total_pages(page_size: u32, total_items: u32) -> u32 { - let total_pages = ((total_items as f64) / (page_size as f64)).ceil() as u32; - - total_pages + ((total_items as f64) / (page_size as f64)).ceil() as u32 } diff --git a/src/handlers/raid_quest.rs b/src/handlers/raid_quest.rs index c806149..cd413ee 100644 --- a/src/handlers/raid_quest.rs +++ b/src/handlers/raid_quest.rs @@ -137,7 +137,7 @@ pub async fn handle_get_raid_leaderboard( meta: PaginationMetadata { page: params.page, page_size: params.page_size, - total_items: total_items as u32, + total_items, total_pages, }, }; @@ -151,9 +151,9 @@ pub async fn handle_get_specific_raider_raid_leaderboard( Path((raider_id, raid_id)): Path<(String, i32)>, ) -> Result>, AppError> { let Some(raider_leaderboard) = state.db.raid_leaderboards.get_raider_entry(raid_id, &raider_id).await? else { - return Err(AppError::Database(DbError::RecordNotFound(format!( - "No raider leaderboard is found" - )))); + return Err(AppError::Database(DbError::RecordNotFound( + "No raider leaderboard is found".to_string(), + ))); }; Ok(SuccessResponse::new(raider_leaderboard)) @@ -189,13 +189,13 @@ pub async fn handle_create_raid_submission( let (current_active_raid, user_x) = get_active_raid_and_x_association(&state, &user).await?; let Some((reply_username, reply_id)) = parse_x_status_url(&payload.tweet_reply_link) else { - return Err(AppError::Handler(HandlerError::InvalidBody(format!( - "Couldn't parse tweet reply link" - )))); + return Err(AppError::Handler(HandlerError::InvalidBody( + "Couldn't parse tweet reply link".to_string(), + ))); }; if user_x.username != reply_username { return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( - format!("Only tweet reply author is eligible to submit"), + "Only tweet reply author is eligible to submit".to_string(), )))); } @@ -224,7 +224,7 @@ pub async fn handle_delete_raid_submission( if raid_submission.raider_id != user.quan_address.0 { return Err(AppError::Handler(HandlerError::Auth(AuthHandlerError::Unauthorized( - format!("Only raid submission owner can delete"), + "Only raid submission owner can delete".to_string(), )))); } @@ -238,14 +238,14 @@ async fn get_active_raid_and_x_association( user: &Address, ) -> Result<(RaidQuest, XAssociation), AppError> { let Some(current_active_raid) = state.db.raid_quests.find_active().await? else { - return Err(AppError::Database(DbError::RecordNotFound(format!( - "No active raid is found" - )))); + return Err(AppError::Database(DbError::RecordNotFound( + "No active raid is found".to_string(), + ))); }; let Some(user_x) = state.db.x_associations.find_by_address(&user.quan_address).await? else { - return Err(AppError::Database(DbError::RecordNotFound(format!( - "User doesn't have X association" - )))); + return Err(AppError::Database(DbError::RecordNotFound( + "User doesn't have X association".to_string(), + ))); }; Ok((current_active_raid, user_x)) } @@ -369,7 +369,7 @@ mod tests { .oneshot( Request::builder() .method("PUT") - .uri(&format!("/raids/{}/finish", raid_id)) + .uri(format!("/raids/{}/finish", raid_id)) .body(Body::empty()) .unwrap(), ) @@ -405,7 +405,7 @@ mod tests { .oneshot( Request::builder() .method("PUT") - .uri(&format!("/raids/{}/activate", raid_id)) + .uri(format!("/raids/{}/activate", raid_id)) .body(Body::empty()) .unwrap(), ) @@ -514,7 +514,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri(&format!("/raiders/{}/leaderboard/{}", user.quan_address.0, raid_id,)) + .uri(format!("/raiders/{}/leaderboard/{}", user.quan_address.0, raid_id,)) .body(Body::empty()) .unwrap(), ) @@ -707,7 +707,7 @@ mod tests { .oneshot( Request::builder() .method("DELETE") - .uri(&format!("/submissions/{}", submission_id)) + .uri(format!("/submissions/{}", submission_id)) .body(Body::empty()) .unwrap(), ) @@ -756,7 +756,7 @@ mod tests { .oneshot( Request::builder() .method("DELETE") - .uri(&format!("/submissions/{}", submission_id)) + .uri(format!("/submissions/{}", submission_id)) .body(Body::empty()) .unwrap(), ) diff --git a/src/handlers/referral.rs b/src/handlers/referral.rs index 14a15c2..a311667 100644 --- a/src/handlers/referral.rs +++ b/src/handlers/referral.rs @@ -68,9 +68,9 @@ pub async fn handle_add_referral( Ok(SuccessResponse::new(referrer.referral_code)) } else { - return Err(AppError::Handler(HandlerError::Referral( + Err(AppError::Handler(HandlerError::Referral( ReferralHandlerError::ReferralNotFound(format!("Referrer not found for code '{}'", submitted_code)), - ))); + ))) } } diff --git a/src/handlers/tweet_author.rs b/src/handlers/tweet_author.rs index 99d41ca..455234c 100644 --- a/src/handlers/tweet_author.rs +++ b/src/handlers/tweet_author.rs @@ -423,7 +423,7 @@ mod tests { let author = state.db.tweet_authors.find_by_id("hello").await.unwrap().unwrap(); - assert_eq!(author.is_ignored, true); + assert!(author.is_ignored); } #[tokio::test] diff --git a/src/metrics.rs b/src/metrics.rs index 005e0f1..c8bb113 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -54,6 +54,12 @@ pub struct Metrics { pub registry: Arc, } +impl Default for Metrics { + fn default() -> Self { + Self::new() + } +} + impl Metrics { pub fn new() -> Self { let registry = Registry::new(); diff --git a/src/models/raid_submission.rs b/src/models/raid_submission.rs index 2e49489..b0c6d88 100644 --- a/src/models/raid_submission.rs +++ b/src/models/raid_submission.rs @@ -76,15 +76,13 @@ impl From<&TwitterTweet> for UpdateRaidSubmissionStats { let default_metrics = TweetPublicMetrics::default(); let public_metrics = tweet.public_metrics.as_ref().unwrap_or(&default_metrics); - let update_payload = UpdateRaidSubmissionStats { + UpdateRaidSubmissionStats { id: tweet.id.clone(), impression_count: public_metrics.impression_count as i32, like_count: public_metrics.like_count as i32, retweet_count: public_metrics.retweet_count as i32, reply_count: public_metrics.reply_count as i32, - }; - - update_payload + } } } diff --git a/src/models/relevant_tweet.rs b/src/models/relevant_tweet.rs index ac686c0..6ee4261 100644 --- a/src/models/relevant_tweet.rs +++ b/src/models/relevant_tweet.rs @@ -97,7 +97,7 @@ impl NewTweetPayload { .unwrap(); let created_at = tweet.created_at.ok_or_else(|| chrono::Utc::now().to_rfc3339()).unwrap(); - let new_tweet = NewTweetPayload { + NewTweetPayload { id: tweet.id, author_id: tweet.author_id.unwrap(), text: tweet.text, @@ -106,8 +106,6 @@ impl NewTweetPayload { retweet_count: public_metrics.retweet_count as i32, reply_count: public_metrics.reply_count as i32, created_at: DateTime::parse_from_rfc3339(&created_at).unwrap().with_timezone(&Utc), - }; - - new_tweet + } } } diff --git a/src/models/tweet_author.rs b/src/models/tweet_author.rs index 0945281..7c89ba0 100644 --- a/src/models/tweet_author.rs +++ b/src/models/tweet_author.rs @@ -86,7 +86,7 @@ impl NewAuthorPayload { pub fn new(author: TwitterUser) -> Self { let public_metrics = author.public_metrics.unwrap_or_default(); - let new_author = NewAuthorPayload { + NewAuthorPayload { id: author.id, name: author.name, username: author.username, @@ -97,9 +97,7 @@ impl NewAuthorPayload { media_count: public_metrics.media_count.unwrap_or(0) as i32, like_count: public_metrics.like_count.unwrap_or(0) as i32, is_ignored: Some(true), - }; - - new_author + } } } diff --git a/src/repositories/address.rs b/src/repositories/address.rs index 1164143..5ebd8b6 100644 --- a/src/repositories/address.rs +++ b/src/repositories/address.rs @@ -110,7 +110,7 @@ impl AddressRepository { .build_query_scalar() .fetch_one(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(count) } @@ -291,7 +291,7 @@ impl AddressRepository { .build_query_as::() .fetch_all(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(addresses) } diff --git a/src/repositories/raid_quest.rs b/src/repositories/raid_quest.rs index 9ac53b3..a253a91 100644 --- a/src/repositories/raid_quest.rs +++ b/src/repositories/raid_quest.rs @@ -92,7 +92,7 @@ impl RaidQuestRepository { .build_query_as::() .fetch_all(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(tweets) } @@ -218,7 +218,7 @@ impl RaidQuestRepository { .build_query_scalar() .fetch_one(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(count) } diff --git a/src/repositories/raid_submission.rs b/src/repositories/raid_submission.rs index e4ba71c..6444ca7 100644 --- a/src/repositories/raid_submission.rs +++ b/src/repositories/raid_submission.rs @@ -378,7 +378,7 @@ mod tests { // 3. Verify Update in DB let updated = repo.find_by_id(&input.id).await.unwrap().unwrap(); - assert_eq!(updated.is_invalid, true); + assert!(updated.is_invalid); // 4. Verify `updated_at` trigger worked assert!( diff --git a/src/repositories/relevant_tweet.rs b/src/repositories/relevant_tweet.rs index acf8810..553d995 100644 --- a/src/repositories/relevant_tweet.rs +++ b/src/repositories/relevant_tweet.rs @@ -87,7 +87,7 @@ impl RelevantTweetRepository { .build_query_scalar() .fetch_one(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(count) } @@ -141,7 +141,7 @@ impl RelevantTweetRepository { .build_query_as::() .fetch_all(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(tweets) } diff --git a/src/repositories/tweet_author.rs b/src/repositories/tweet_author.rs index bf2b1fc..f90204b 100644 --- a/src/repositories/tweet_author.rs +++ b/src/repositories/tweet_author.rs @@ -66,7 +66,7 @@ impl TweetAuthorRepository { .build_query_scalar() .fetch_one(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(count) } @@ -102,7 +102,7 @@ impl TweetAuthorRepository { .build_query_as::() .fetch_all(&self.pool) .await - .map_err(|e| DbError::Database(e))?; + .map_err(DbError::Database)?; Ok(authors) } @@ -150,7 +150,7 @@ impl TweetAuthorRepository { .bind(&payload.id) .bind(&payload.name) .bind(&payload.username) - .bind(&payload.is_ignored) + .bind(payload.is_ignored) .bind(payload.followers_count) .bind(payload.following_count) .bind(payload.tweet_count) diff --git a/src/services/graphql_client.rs b/src/services/graphql_client.rs index 16b8481..f7ee0eb 100644 --- a/src/services/graphql_client.rs +++ b/src/services/graphql_client.rs @@ -1,7 +1,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use tracing::{debug, error, info, warn}; +use tracing::{debug, info, warn}; use crate::{ db_persistence::{DbError, DbPersistence}, @@ -1012,7 +1012,7 @@ query GetEventCountByIds($ids: [String!]!) { unique_addresses.insert(&transfer.to.id); } - assert!(unique_addresses.len() >= 1); + assert!(!unique_addresses.is_empty()); } // ============================================================================ diff --git a/src/services/raid_leaderboard_service.rs b/src/services/raid_leaderboard_service.rs index 6c19541..b09d558 100644 --- a/src/services/raid_leaderboard_service.rs +++ b/src/services/raid_leaderboard_service.rs @@ -83,7 +83,7 @@ impl RaidLeaderboardService { .await?; if raid_submissions.is_empty() { tracing::info!("No raid submissions found yet for current active raid quest."); - return Ok(Default::default()); + return Ok(()); }; let queries = RaidLeaderboardService::build_batched_tweet_queries(&raid_submissions); @@ -139,7 +139,7 @@ impl RaidLeaderboardService { // `for tweet in tweets` consumes the original vector, so we "move" // the data instead of cloning it. for tweet in tweets { - let is_valid_reply = tweet.referenced_tweets.as_ref().map_or(false, |refs| { + let is_valid_reply = tweet.referenced_tweets.as_ref().is_some_and(|refs| { // Check if ANY of the referenced IDs exist in our valid set refs.iter().any(|r| valid_raid_ids.contains(&r.id)) }); @@ -369,7 +369,7 @@ mod tests { let updated_sub = db.raid_submissions.find_by_id(sub_id).await.unwrap().unwrap(); assert!(updated_sub.updated_at > updated_sub.created_at); - assert_eq!(updated_sub.is_invalid, false); + assert!(!updated_sub.is_invalid); assert_eq!(updated_sub.impression_count, 100); assert_eq!(updated_sub.like_count, 50); } @@ -430,7 +430,7 @@ mod tests { let updated_sub = db.raid_submissions.find_by_id(sub_id).await.unwrap().unwrap(); assert!(updated_sub.updated_at > updated_sub.created_at); - assert_eq!(updated_sub.is_invalid, true); + assert!(updated_sub.is_invalid); assert_eq!(updated_sub.impression_count, 0); assert_eq!(updated_sub.like_count, 0); } diff --git a/src/services/telegram_service.rs b/src/services/telegram_service.rs index d494869..e01e4c0 100644 --- a/src/services/telegram_service.rs +++ b/src/services/telegram_service.rs @@ -81,10 +81,7 @@ impl TelegramService { Ok(()) } Err(err) => { - let status = err - .status() - .unwrap_or_else(|| StatusCode::INTERNAL_SERVER_ERROR) - .as_u16(); + let status = err.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR).as_u16(); let body = err.to_string(); Err(AppError::Telegram(status, body)) diff --git a/src/utils/eth_address_validator.rs b/src/utils/eth_address_validator.rs index 33264c0..cbeeba0 100644 --- a/src/utils/eth_address_validator.rs +++ b/src/utils/eth_address_validator.rs @@ -42,8 +42,8 @@ pub fn is_valid_eth_address(address: &str) -> bool { } // If the address is all lowercase or all uppercase, it's valid (no checksum) - let is_all_lowercase = addr_part.chars().all(|c| c.is_lowercase() || c.is_digit(10)); - let is_all_uppercase = addr_part.chars().all(|c| c.is_uppercase() || c.is_digit(10)); + let is_all_lowercase = addr_part.chars().all(|c| c.is_lowercase() || c.is_ascii_digit()); + let is_all_uppercase = addr_part.chars().all(|c| c.is_uppercase() || c.is_ascii_digit()); if is_all_lowercase || is_all_uppercase { return true; diff --git a/src/utils/jwt.rs b/src/utils/jwt.rs index 9ab42c3..7129028 100644 --- a/src/utils/jwt.rs +++ b/src/utils/jwt.rs @@ -22,22 +22,14 @@ pub fn extract_jwt_token_from_request(req: &Request) -> Result AppState { let db = Arc::new(db); - return AppState { + AppState { db, metrics: Arc::new(Metrics::new()), graphql_client: Arc::new(graphql_client), @@ -23,7 +23,7 @@ pub async fn create_test_app_state() -> AppState { oauth_sessions: Arc::new(Mutex::new(std::collections::HashMap::new())), twitter_oauth_tokens: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())), challenges: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())), - }; + } } pub fn generate_test_token(secret: &str, user_id: &str) -> String { diff --git a/src/utils/x_url.rs b/src/utils/x_url.rs index e4c895a..a882455 100644 --- a/src/utils/x_url.rs +++ b/src/utils/x_url.rs @@ -14,7 +14,7 @@ pub fn parse_x_status_url(url: &str) -> Option<(String, String)> { // Extract Username let prefix = &url_path[..index]; - let username = prefix.split('/').last()?; + let username = prefix.split('/').next_back()?; if username.is_empty() { return None;