diff --git a/Cargo.toml b/Cargo.toml index f334bae..88c6286 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "trainee-tracker" version = "0.1.0" -edition = "2021" +edition = "2024" default-run = "trainee-tracker" [dependencies] diff --git a/src/auth.rs b/src/auth.rs index d1e9f04..282faa8 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context}; +use anyhow::{Context, anyhow}; use askama::Template; use axum::{ extract::{Query, State}, @@ -11,8 +11,8 @@ use tower_sessions::Session; use uuid::Uuid; use crate::{ - slack::{make_slack_redirect_uri, SLACK_ACCESS_TOKEN_SESSION_KEY}, Config, Error, ServerState, + slack::{SLACK_ACCESS_TOKEN_SESSION_KEY, make_slack_redirect_uri}, }; #[derive(Deserialize)] @@ -56,7 +56,10 @@ pub(crate) async fn github_auth_redirect_url( original_uri: Uri, ) -> Result { let uuid = Uuid::new_v4(); - let redirect_url = format!("https://github.com/login/oauth/authorize?client_id={}&redirect_uri={}/api/oauth-callbacks/github&scope=read:user%20read:org&state={}", server_state.config.github_client_id, server_state.config.public_base_url, uuid); + let redirect_url = format!( + "https://github.com/login/oauth/authorize?client_id={}&redirect_uri={}/api/oauth-callbacks/github&scope=read:user%20read:org&state={}", + server_state.config.github_client_id, server_state.config.public_base_url, uuid + ); server_state .github_auth_state_cache .insert(uuid, original_uri) @@ -93,14 +96,15 @@ pub async fn handle_google_oauth_callback( session: Session, params: Query, ) -> Result, Error> { - let auth_state = if let Some(auth_state) = server_state + let auth_state = match server_state .google_auth_state_cache .remove(¶ms.state) .await { - auth_state - } else { - return Err(Error::Fatal(anyhow!("Unrecognised state"))); + Some(auth_state) => auth_state, + None => { + return Err(Error::Fatal(anyhow!("Unrecognised state"))); + } }; let redirect_uri = format!( diff --git a/src/bin/match-pr-to-assignment.rs b/src/bin/match-pr-to-assignment.rs index d8efb02..e370a25 100644 --- a/src/bin/match-pr-to-assignment.rs +++ b/src/bin/match-pr-to-assignment.rs @@ -21,8 +21,17 @@ async fn main() { let octocrab = octocrab_for_token(github_token.to_owned()).expect("Failed to get octocrab"); - let Ok([_https, _scheme, _githubdotcom, org_name, module_name, _pull, pr_number_str]) = - <[_; 7]>::try_from(pr_link.split('/').collect::>()) + let Ok( + [ + _https, + _scheme, + _githubdotcom, + org_name, + module_name, + _pull, + pr_number_str, + ], + ) = <[_; 7]>::try_from(pr_link.split('/').collect::>()) else { panic!("Couldn't parse GitHub PR link {}", pr_link); }; diff --git a/src/bin/pr-metadata-validator.rs b/src/bin/pr-metadata-validator.rs index d9d5b16..f0cc5f0 100644 --- a/src/bin/pr-metadata-validator.rs +++ b/src/bin/pr-metadata-validator.rs @@ -6,12 +6,12 @@ use maplit::btreemap; use octocrab::Octocrab; use regex::Regex; use trainee_tracker::{ + Error, config::{CourseSchedule, CourseScheduleWithRegisterSheetId}, course::match_prs_to_assignments, newtypes::Region, octocrab::octocrab_for_token, prs::get_prs, - Error, }; const ARBITRARY_REGION: Region = Region(String::new()); @@ -24,7 +24,15 @@ async fn main() { }; let pr_parts: Vec<_> = pr_url.split("/").collect(); let (github_org_name, module_name, pr_number) = match pr_parts.as_slice() { - [_http, _scheme, _domain, github_org_name, module_name, _pull, number] => ( + [ + _http, + _scheme, + _domain, + github_org_name, + module_name, + _pull, + number, + ] => ( (*github_org_name).to_owned(), (*module_name).to_owned(), number.parse::().expect("Failed to parse PR number"), @@ -78,7 +86,9 @@ async fn main() { ValidationResult::UnknownRegion => UNKNOWN_REGION_COMMENT, }; - let full_message = format!("{message}\n\nIf this PR is not coursework, please add the NotCoursework label (and message on Slack in #cyf-curriculum or it will probably not be noticed).\n\nIf this PR needs reviewed, please add the 'Needs Review' label to this PR after you have resolved the issues listed above."); + let full_message = format!( + "{message}\n\nIf this PR is not coursework, please add the NotCoursework label (and message on Slack in #cyf-curriculum or it will probably not be noticed).\n\nIf this PR needs reviewed, please add the 'Needs Review' label to this PR after you have resolved the issues listed above." + ); eprintln!("{}", full_message); octocrab .issues(&github_org_name, &module_name) @@ -201,7 +211,12 @@ async fn validate_pr( let sprint_regex = Regex::new(r"^(S|s)print \d+$").unwrap(); let sprint_section = title_sections[3].trim(); if !sprint_regex.is_match(sprint_section) { - return Ok(ValidationResult::BadTitleFormat { reason: format!("Sprint part ({}) doesn't match expected format (example: 'Sprint 2', without quotes)", sprint_section) }); + return Ok(ValidationResult::BadTitleFormat { + reason: format!( + "Sprint part ({}) doesn't match expected format (example: 'Sprint 2', without quotes)", + sprint_section + ), + }); } if pr_in_question.title.to_ascii_uppercase() == pr_in_question.title { diff --git a/src/course.rs b/src/course.rs index ede7ffe..93dd8aa 100644 --- a/src/course.rs +++ b/src/course.rs @@ -5,15 +5,15 @@ use std::{ }; use crate::{ + Error, config::CourseScheduleWithRegisterSheetId, - github_accounts::{get_trainees, Trainee}, - mentoring::{get_mentoring_records, MentoringRecord}, + github_accounts::{Trainee, get_trainees}, + mentoring::{MentoringRecord, get_mentoring_records}, newtypes::{GithubLogin, Region}, octocrab::all_pages, - prs::{get_prs, Pr, PrState}, - register::{get_register, Register}, + prs::{Pr, PrState, get_prs}, + register::{Register, get_register}, sheets::SheetsClient, - Error, }; use anyhow::Context; use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; @@ -23,8 +23,8 @@ use futures::future::join_all; use indexmap::{IndexMap, IndexSet}; use maplit::btreemap; use octocrab::{ - models::{issues::Issue, teams::RequestedTeam, Author}, Octocrab, + models::{Author, issues::Issue, teams::RequestedTeam}, }; use regex::Regex; use serde::Serialize; @@ -165,7 +165,7 @@ fn parse_issue(issue: &Issue) -> Result return Err(Error::UserFacing(format!( "Failed to parse issue {} - sprint label wasn't (non-zero) number: {}", html_url, label.name - ))) + ))); } } } @@ -901,7 +901,10 @@ pub fn match_prs_to_assignments( let number = usize::from_str(number_str) .with_context(|| format!("Failed to parse '{}' as number", number_str))?; if number == 0 || number > 20 { - return Err(Error::Fatal(anyhow::anyhow!("Sprint number was impractical - expected something between 1 and 20 but was {}", number))); + return Err(Error::Fatal(anyhow::anyhow!( + "Sprint number was impractical - expected something between 1 and 20 but was {}", + number + ))); } sprint_index = Some(number - 1); diff --git a/src/endpoints.rs b/src/endpoints.rs index 0e4d2a9..89d97b8 100644 --- a/src/endpoints.rs +++ b/src/endpoints.rs @@ -1,11 +1,11 @@ use std::collections::BTreeMap; -use ::octocrab::models::{teams::RequestedTeam, Author}; +use ::octocrab::models::{Author, teams::RequestedTeam}; use anyhow::Context; use axum::{ + Json, extract::{OriginalUri, Path, State}, response::IntoResponse, - Json, }; use futures::future::join_all; use http::HeaderMap; @@ -14,13 +14,13 @@ use serde::Serialize; use tower_sessions::Session; use crate::{ + Error, ServerState, github_accounts::get_trainees, newtypes::GithubLogin, octocrab::{all_pages, octocrab}, - prs::{fill_in_reviewers, get_prs, PrWithReviews}, - register::{get_register, Attendance}, + prs::{PrWithReviews, fill_in_reviewers, get_prs}, + register::{Attendance, get_register}, sheets::sheets_client, - Error, ServerState, }; pub async fn health_check() -> impl IntoResponse { diff --git a/src/frontend.rs b/src/frontend.rs index 8681d8e..fef7bea 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -7,23 +7,23 @@ use axum::{ response::{Html, IntoResponse, Response}, }; use futures::future::join_all; -use http::{header::CONTENT_TYPE, HeaderMap, StatusCode, Uri}; +use http::{HeaderMap, StatusCode, Uri, header::CONTENT_TYPE}; use serde::Deserialize; use tower_sessions::Session; use crate::{ + Error, ServerState, config::CourseScheduleWithRegisterSheetId, course::{ - fetch_batch_metadata, get_batch_with_submissions, Attendance, Batch, BatchMetadata, Course, - Submission, TraineeStatus, + Attendance, Batch, BatchMetadata, Course, Submission, TraineeStatus, fetch_batch_metadata, + get_batch_with_submissions, }, - google_groups::{get_groups, groups_client, GoogleGroup}, + google_groups::{GoogleGroup, get_groups, groups_client}, octocrab::octocrab, prs::{MaybeReviewerStaffOnlyDetails, PrState, ReviewerInfo}, reviewer_staff_info::get_reviewer_staff_info, sheets::sheets_client, slack::list_groups_with_members, - Error, ServerState, }; pub async fn list_courses( diff --git a/src/github_accounts.rs b/src/github_accounts.rs index bebbf8d..bc9f1ab 100644 --- a/src/github_accounts.rs +++ b/src/github_accounts.rs @@ -6,9 +6,9 @@ use serde::{Deserialize, Serialize}; use sheets::types::Sheet; use crate::{ - newtypes::{new_case_insensitive_email_address, GithubLogin, Region}, - sheets::{cell_string, SheetsClient}, Error, + newtypes::{GithubLogin, Region, new_case_insensitive_email_address}, + sheets::{SheetsClient, cell_string}, }; // TODO: Replace this with a serde implementation from a Google Sheet. @@ -67,7 +67,11 @@ fn trainees_from_sheet(sheet: &Sheet) -> Result, let mut trainees = BTreeMap::new(); for data in &sheet.data { if data.start_column != 0 || data.start_row != 0 { - return Err(Error::Fatal(anyhow::anyhow!("Reading data from Google Sheets API - got data chunk that didn't start at row=0,column=0 - got row={},column={}", data.start_row, data.start_column))); + return Err(Error::Fatal(anyhow::anyhow!( + "Reading data from Google Sheets API - got data chunk that didn't start at row=0,column=0 - got row={},column={}", + data.start_row, + data.start_column + ))); } for (row_index, row) in data.row_data.iter().enumerate() { if row_index == 0 { @@ -75,7 +79,10 @@ fn trainees_from_sheet(sheet: &Sheet) -> Result, } let cells = &row.values; if cells.len() < 5 { - return Err(Error::Fatal(anyhow::anyhow!("Reading trainee data from Google Sheets API, row {} didn't have at least 5 columns", row_index))); + return Err(Error::Fatal(anyhow::anyhow!( + "Reading trainee data from Google Sheets API, row {} didn't have at least 5 columns", + row_index + ))); } let github_login = GithubLogin::from(cell_string(&cells[3])); diff --git a/src/google_groups.rs b/src/google_groups.rs index 82305eb..c1e23a0 100644 --- a/src/google_groups.rs +++ b/src/google_groups.rs @@ -4,16 +4,16 @@ use anyhow::Context; use email_address::EmailAddress; use futures::future::join_all; use gsuite_api::{ - types::{Group, Member}, Client, Response, + types::{Group, Member}, }; use http::Uri; use tower_sessions::Session; use crate::{ - google_auth::{make_redirect_uri, redirect_endpoint, GoogleScope}, - newtypes::new_case_insensitive_email_address, Error, ServerState, + google_auth::{GoogleScope, make_redirect_uri, redirect_endpoint}, + newtypes::new_case_insensitive_email_address, }; pub async fn groups_client( diff --git a/src/mentoring.rs b/src/mentoring.rs index b5b4243..6d38ebf 100644 --- a/src/mentoring.rs +++ b/src/mentoring.rs @@ -1,4 +1,4 @@ -use std::collections::{btree_map::Entry, BTreeMap}; +use std::collections::{BTreeMap, btree_map::Entry}; use anyhow::Context; use chrono::{NaiveDate, Utc}; @@ -7,8 +7,8 @@ use sheets::types::GridData; use tracing::warn; use crate::{ - sheets::{cell_date, cell_string, SheetsClient}, Error, + sheets::{SheetsClient, cell_date, cell_string}, }; pub struct MentoringRecords { diff --git a/src/octocrab.rs b/src/octocrab.rs index 4ade028..480cb45 100644 --- a/src/octocrab.rs +++ b/src/octocrab.rs @@ -1,22 +1,22 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context; -use http::{header::USER_AGENT, HeaderValue, Uri}; +use http::{HeaderValue, Uri, header::USER_AGENT}; use hyper_rustls::HttpsConnectorBuilder; use octocrab::{ + AuthState, Octocrab, OctocrabBuilder, service::middleware::{ auth_header::AuthHeaderLayer, base_uri::BaseUriLayer, extra_headers::ExtraHeadersLayer, retry::RetryConfig, }, - AuthState, Octocrab, OctocrabBuilder, }; use serde::de::DeserializeOwned; use tower::retry::RetryLayer; use tower_sessions::Session; use crate::{ - auth::{github_auth_redirect_url, GITHUB_ACCESS_TOKEN_SESSION_KEY}, Error, ServerState, + auth::{GITHUB_ACCESS_TOKEN_SESSION_KEY, github_auth_redirect_url}, }; pub(crate) async fn octocrab( diff --git a/src/prs.rs b/src/prs.rs index db4b160..48bbdc6 100644 --- a/src/prs.rs +++ b/src/prs.rs @@ -3,14 +3,14 @@ use std::collections::{BTreeMap, BTreeSet}; use anyhow::Context; use chrono::{DateTime, TimeDelta}; use futures::future::join_all; +use octocrab::Octocrab; use octocrab::models::pulls::{Comment, PullRequest, Review as OctoReview}; use octocrab::models::{Author, IssueState}; use octocrab::params::State; -use octocrab::Octocrab; use serde::Serialize; -use crate::newtypes::GithubLogin; use crate::Error; +use crate::newtypes::GithubLogin; #[derive(Clone, Debug, PartialEq, Eq, Serialize)] pub struct Pr { diff --git a/src/register.rs b/src/register.rs index 8465e32..a79c3e6 100644 --- a/src/register.rs +++ b/src/register.rs @@ -7,9 +7,9 @@ use sheets::types::{CellData, GridData}; use tracing::warn; use crate::{ - newtypes::new_case_insensitive_email_address, - sheets::{cell_string, SheetsClient}, Error, + newtypes::new_case_insensitive_email_address, + sheets::{SheetsClient, cell_string}, }; #[derive(Debug)] diff --git a/src/reviewer_staff_info.rs b/src/reviewer_staff_info.rs index 0661727..afbfbaf 100644 --- a/src/reviewer_staff_info.rs +++ b/src/reviewer_staff_info.rs @@ -3,10 +3,10 @@ use std::collections::BTreeMap; use sheets::types::Sheet; use crate::{ + Error, newtypes::GithubLogin, prs::{CheckStatus, ReviewerStaffOnlyDetails}, - sheets::{cell_bool, cell_string, SheetsClient}, - Error, + sheets::{SheetsClient, cell_bool, cell_string}, }; pub(crate) async fn get_reviewer_staff_info( @@ -58,7 +58,11 @@ fn reviewer_staff_detail_from_sheet( let mut reviewers = BTreeMap::new(); for data in &sheet.data { if data.start_column != 0 || data.start_row != 0 { - return Err(Error::Fatal(anyhow::anyhow!("Reading data from Google Sheets API - got data chunk that didn't start at row=0,column=0 - got row={},column={}", data.start_row, data.start_column))); + return Err(Error::Fatal(anyhow::anyhow!( + "Reading data from Google Sheets API - got data chunk that didn't start at row=0,column=0 - got row={},column={}", + data.start_row, + data.start_column + ))); } for (row_index, row) in data.row_data.iter().enumerate() { if row_index == 0 { diff --git a/src/sheets.rs b/src/sheets.rs index e52ae37..c284803 100644 --- a/src/sheets.rs +++ b/src/sheets.rs @@ -4,8 +4,8 @@ use sheets::{spreadsheets::Spreadsheets, types::CellData}; use tower_sessions::Session; use crate::{ - google_auth::{make_redirect_uri, redirect_endpoint, GoogleScope}, Error, ServerState, + google_auth::{GoogleScope, make_redirect_uri, redirect_endpoint}, }; pub(crate) fn cell_string(cell: &CellData) -> String { @@ -46,7 +46,7 @@ pub(crate) async fn sheets_client( return Err(Error::UserFacing(format!( "Invalid {} header: {}", AUTHORIZATION_HEADER, e - ))) + ))); } }; token