diff --git a/migrations/00000000000000_diesel_initial_setup/down.sql b/migrations/00000000000000_diesel_initial_setup/down.sql index a9f5260..2379ff6 100644 --- a/migrations/00000000000000_diesel_initial_setup/down.sql +++ b/migrations/00000000000000_diesel_initial_setup/down.sql @@ -1,4 +1,4 @@ --- This file was automatically created by Diesel to setup helper functions +-- This file was automatically created by Diesel to set up helper functions -- and other internal bookkeeping. This file is safe to edit, any future -- changes will be added to existing projects as new migrations. diff --git a/migrations/00000000000000_diesel_initial_setup/up.sql b/migrations/00000000000000_diesel_initial_setup/up.sql index d68895b..0e346e9 100644 --- a/migrations/00000000000000_diesel_initial_setup/up.sql +++ b/migrations/00000000000000_diesel_initial_setup/up.sql @@ -1,4 +1,4 @@ --- This file was automatically created by Diesel to setup helper functions +-- This file was automatically created by Diesel to set up helper functions -- and other internal bookkeeping. This file is safe to edit, any future -- changes will be added to existing projects as new migrations. diff --git a/src/app_state.rs b/src/app_state.rs index 1ea5094..0fc34a7 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -50,7 +50,7 @@ impl Schedule { } } -/// Общие данные передаваемые в эндпоинты +/// Common data provided to endpoints. pub struct AppState { pub downloader: Mutex, pub schedule: Mutex>, @@ -76,7 +76,7 @@ impl AppState { } } -/// Создание нового объекта web::Data +/// Create a new object web::Data. pub async fn app_state() -> web::Data { web::Data::new(AppState::new().await) } diff --git a/src/database/models.rs b/src/database/models.rs index 8f0c0a1..39295c0 100644 --- a/src/database/models.rs +++ b/src/database/models.rs @@ -31,28 +31,28 @@ pub enum UserRole { #[diesel(table_name = crate::database::schema::users)] #[diesel(treat_none_as_null = true)] pub struct User { - /// UUID аккаунта + /// Account UUID. pub id: String, - /// Имя пользователя + /// User name. pub username: String, - /// BCrypt хеш пароля + /// BCrypt password hash. pub password: String, - /// Идентификатор привязанного аккаунта VK + /// ID of the linked VK account. pub vk_id: Option, - /// JWT токен доступа + /// JWT access token. pub access_token: String, - /// Группа + /// Group. pub group: String, - /// Роль + /// Role. pub role: UserRole, - /// Версия установленного приложения Polytechnic+ + /// Version of the installed Polytechnic+ application. pub version: String, } @@ -73,12 +73,12 @@ pub struct User { #[diesel(table_name = crate::database::schema::fcm)] #[diesel(primary_key(user_id))] pub struct FCM { - /// UUID аккаунта. + /// Account UUID. pub user_id: String, - /// FCM токен. + /// FCM token. pub token: String, - /// Список топиков, на которые подписан пользователь. + /// List of topics subscribed to by the user. pub topics: Vec>, } diff --git a/src/extractors/authorized_user.rs b/src/extractors/authorized_user.rs index 152000b..7d4f4e8 100644 --- a/src/extractors/authorized_user.rs +++ b/src/extractors/authorized_user.rs @@ -16,19 +16,19 @@ use std::fmt::Debug; #[status_code = "actix_web::http::StatusCode::UNAUTHORIZED"] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum Error { - /// В запросе отсутствует заголовок Authorization + /// There is no Authorization header in the request. #[display("No Authorization header found")] NoHeader, - /// Неизвестный тип авторизации, отличающийся от Bearer + /// Unknown authorization type other than Bearer. #[display("Bearer token is required")] UnknownAuthorizationType, - /// Токен не действителен + /// Invalid or expired access token. #[display("Invalid or expired access token")] InvalidAccessToken, - /// Пользователь привязанный к токену не найден в базе данных + /// The user bound to the token is not found in the database. #[display("No user associated with access token")] NoUser, } @@ -39,7 +39,7 @@ impl Error { } } -/// Экстрактор пользователя из запроса с токеном +/// User extractor from request with Bearer access token. impl FromRequestSync for User { type Error = actix_web::Error; @@ -87,7 +87,7 @@ impl UserExtractor<{ FCM }> { } } -/// Экстрактор пользователя и дополнительных параметров из запроса с токеном +/// Extractor of user and additional parameters from request with Bearer token. impl FromRequestSync for UserExtractor<{ FCM }> { type Error = actix_web::Error; diff --git a/src/extractors/base.rs b/src/extractors/base.rs index 608c22a..f487e9d 100644 --- a/src/extractors/base.rs +++ b/src/extractors/base.rs @@ -2,26 +2,64 @@ use actix_web::dev::Payload; use actix_web::{FromRequest, HttpRequest}; use futures_util::future::LocalBoxFuture; use std::future::{Ready, ready}; +use std::ops; -/// Асинхронный экстрактор объектов из запроса +/// # Async extractor. + +/// Asynchronous object extractor from a query. pub struct AsyncExtractor(T); impl AsyncExtractor { #[allow(dead_code)] - /// Получение объекта, извлечённого с помощью экстрактора + /// Retrieve the object extracted with the extractor. pub fn into_inner(self) -> T { self.0 } } +impl ops::Deref for AsyncExtractor { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ops::DerefMut for AsyncExtractor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + pub trait FromRequestAsync: Sized { type Error: Into; - /// Асинхронная функция для извлечения данных из запроса + /// Asynchronous function for extracting data from a query. + /// + /// returns: Result + /// + /// # Examples + /// + /// ``` + /// struct User { + /// pub id: String, + /// pub username: String, + /// } + /// + /// // TODO: Я вообще этот экстрактор не использую, нахуя мне тогда писать пример, если я не ебу как его использовать. Я забыл. + /// + /// #[get("/")] + /// fn get_user_async( + /// user: web::AsyncExtractor, + /// ) -> web::Json { + /// let user = user.into_inner(); + /// + /// web::Json(user) + /// } + /// ``` async fn from_request_async(req: HttpRequest, payload: Payload) -> Result; } -/// Реализация треита FromRequest для всех асинхронных экстракторов impl FromRequest for AsyncExtractor { type Error = T::Error; type Future = LocalBoxFuture<'static, Result>; @@ -37,24 +75,72 @@ impl FromRequest for AsyncExtractor { } } -/// Синхронный экстрактор объектов из запроса +/// # Sync extractor. + +/// Synchronous object extractor from a query. pub struct SyncExtractor(T); impl SyncExtractor { - /// Получение объекта, извлечённого с помощью экстрактора + /// Retrieving an object extracted with the extractor. pub fn into_inner(self) -> T { self.0 } } +impl ops::Deref for SyncExtractor { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ops::DerefMut for SyncExtractor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + pub trait FromRequestSync: Sized { type Error: Into; - /// Синхронная функция для извлечения данных из запроса + /// Synchronous function for extracting data from a query. + /// + /// returns: Result + /// + /// # Examples + /// + /// ``` + /// struct User { + /// pub id: String, + /// pub username: String, + /// } + /// + /// impl FromRequestSync for User { + /// type Error = actix_web::Error; + /// + /// fn from_request_sync(req: &HttpRequest, _: &mut Payload) -> Result { + /// // do magic here. + /// + /// Ok(User { + /// id: "qwerty".to_string(), + /// username: "n08i40k".to_string() + /// }) + /// } + /// } + /// + /// #[get("/")] + /// fn get_user_sync( + /// user: web::SyncExtractor, + /// ) -> web::Json { + /// let user = user.into_inner(); + /// + /// web::Json(user) + /// } + /// ``` fn from_request_sync(req: &HttpRequest, payload: &mut Payload) -> Result; } -/// Реализация треита FromRequest для всех синхронных экстракторов impl FromRequest for SyncExtractor { type Error = T::Error; type Future = Ready>; diff --git a/src/middlewares/authorization.rs b/src/middlewares/authorization.rs index 6f5b32b..12c775d 100644 --- a/src/middlewares/authorization.rs +++ b/src/middlewares/authorization.rs @@ -7,7 +7,7 @@ use actix_web::{Error, HttpRequest, ResponseError}; use futures_util::future::LocalBoxFuture; use std::future::{Ready, ready}; -/// Middleware guard работающий с токенами JWT +/// Middleware guard working with JWT tokens. pub struct JWTAuthorization; impl Transform for JWTAuthorization @@ -31,13 +31,13 @@ pub struct JWTAuthorizationMiddleware { service: S, } -/// Функция для проверки наличия и действительности токена в запросе, а так же существования пользователя к которому он привязан impl JWTAuthorizationMiddleware where S: Service, Error = Error>, S::Future: 'static, B: 'static, { + /// Checking the validity of the token. pub fn check_authorization( &self, req: &HttpRequest, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 19773b3..f72fb2b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -14,39 +14,37 @@ use std::sync::LazyLock; pub mod schema; -/// Данные ячейке хранящей строку +/// Data cell storing the line. struct InternalId { - /// Индекс строки + /// Line index. row: u32, - /// Индекс столбца + /// Column index. column: u32, - /** - * Текст в ячейке - */ + /// Text in the cell. name: String, } -/// Данные о времени проведения пар из второй колонки расписания +/// Data on the time of lessons from the second column of the schedule. struct InternalTime { - /// Временной отрезок проведения пары + /// Temporary segment of the lesson. time_range: LessonTime, - /// Тип пары + /// Type of lesson. lesson_type: LessonType, - /// Индекс пары + /// The lesson index. default_index: Option, - /// Рамка ячейки + /// The frame of the cell. xls_range: ((u32, u32), (u32, u32)), } -/// Сокращение типа рабочего листа +/// Working sheet type alias. type WorkSheet = calamine::Range; -/// Получение строки из требуемой ячейки +/// Getting a line from the required cell. fn get_string_from_cell(worksheet: &WorkSheet, row: u32, col: u32) -> Option { let cell_data = if let Some(data) = worksheet.get((row as usize, col as usize)) { data.to_string() @@ -74,7 +72,7 @@ fn get_string_from_cell(worksheet: &WorkSheet, row: u32, col: u32) -> Option ((u32, u32), (u32, u32)) { let worksheet_end = worksheet.end().unwrap(); @@ -109,7 +107,7 @@ fn get_merge_from_start(worksheet: &WorkSheet, row: u32, column: u32) -> ((u32, ((row, column), (row_end, column_end)) } -/// Получение "скелета" расписания из рабочего листа +/// Obtaining a "skeleton" schedule from the working sheet. fn parse_skeleton(worksheet: &WorkSheet) -> Result<(Vec, Vec), ParseError> { let range = &worksheet; @@ -167,19 +165,20 @@ fn parse_skeleton(worksheet: &WorkSheet) -> Result<(Vec, Vec), - - /// Улица на которой находится корпус политехникума + + /// Street on which the Polytechnic Corps is located. Street(String), } trait StringInnerSlice { - /// Получения отрезка строки из строки по начальному и конечному индексу + /// Obtaining a line from the line on the initial and final index. fn inner_slice(&self, from: usize, to: usize) -> Self; } @@ -192,7 +191,8 @@ impl StringInnerSlice for String { } } -/// Получение нестандартного типа пары по названию +// noinspection GrazieInspection +/// Obtaining a non-standard type of lesson by name. fn guess_lesson_type(name: &String) -> Option<(String, LessonType)> { let map: HashMap = HashMap::from([ ("(консультация)".to_string(), LessonType::Consultation), @@ -234,7 +234,7 @@ fn guess_lesson_type(name: &String) -> Option<(String, LessonType)> { } } -/// Получение пары или улицы из ячейки +/// Getting a pair or street from a cell. fn parse_lesson( worksheet: &WorkSheet, day: &mut Day, @@ -369,7 +369,7 @@ fn parse_lesson( ]))) } -/// Получение списка кабинетов справа от ячейки пары +/// Obtaining a list of cabinets to the right of the lesson cell. fn parse_cabinets(worksheet: &WorkSheet, row: u32, column: u32) -> Vec { let mut cabinets: Vec = Vec::new(); @@ -387,7 +387,7 @@ fn parse_cabinets(worksheet: &WorkSheet, row: u32, column: u32) -> Vec { cabinets } -/// Получение "чистого" названия пары и списка преподавателей из текста ячейки пары +/// Getting the "pure" name of the lesson and list of teachers from the text of the lesson cell. fn parse_name_and_subgroups(name: &String) -> Result<(String, Vec), ParseError> { static LESSON_RE: LazyLock Regex> = LazyLock::new(|| Regex::new(r"(?:[А-Я][а-я]+[А-Я]{2}(?:\([0-9][а-я]+\))?)+$").unwrap()); @@ -479,7 +479,7 @@ fn parse_name_and_subgroups(name: &String) -> Result<(String, Vec, ) -> HashMap { @@ -556,7 +556,24 @@ fn convert_groups_to_teachers( teachers } -/// Чтение XLS документа из буфера и преобразование его в готовые к использованию расписания +/// Reading XLS Document from the buffer and converting it into the schedule ready to use. +/// +/// # Arguments +/// +/// * `buffer`: XLS data containing schedule. +/// +/// returns: Result +/// +/// # Examples +/// +/// ``` +/// let result = parse_xls(&include_bytes!("../../schedule.xls").to_vec()); +/// +/// assert!(result.is_ok()); +/// +/// assert_ne!(result.as_ref().unwrap().groups.len(), 0); +/// assert_ne!(result.as_ref().unwrap().teachers.len(), 0); +/// ``` pub fn parse_xls(buffer: &Vec) -> Result { let cursor = Cursor::new(&buffer); let mut workbook: Xls<_> = diff --git a/src/parser/schema.rs b/src/parser/schema.rs index b25c8da..2a88fa3 100644 --- a/src/parser/schema.rs +++ b/src/parser/schema.rs @@ -6,137 +6,139 @@ use std::collections::HashMap; use std::sync::Arc; use utoipa::ToSchema; +/// The beginning and end of the lesson. #[derive(Clone, Hash, Debug, Serialize, Deserialize, ToSchema)] pub struct LessonTime { - /// Начало пары + /// The beginning of a lesson. pub start: DateTime, - /// Конец пары + /// The end of the lesson. pub end: DateTime, } +/// Type of lesson. #[derive(Clone, Hash, PartialEq, Debug, Serialize_repr, Deserialize_repr, ToSchema)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[repr(u8)] pub enum LessonType { - /// Обычная + /// Обычная. Default = 0, - /// Допы + /// Допы. Additional, - /// Перемена + /// Перемена. Break, - /// Консультация + /// Консультация. Consultation, - /// Самостоятельная работа + /// Самостоятельная работа. IndependentWork, - /// Зачёт + /// Зачёт. Exam, - /// Зачет с оценкой + /// Зачёт с оценкой. ExamWithGrade, - /// Экзамен + /// Экзамен. ExamDefault, } #[derive(Clone, Hash, Debug, Serialize, Deserialize, ToSchema)] pub struct LessonSubGroup { - /// Номер подгруппы + /// Index of subgroup. pub number: u8, - /// Кабинет, если присутствует + /// Cabinet, if present. pub cabinet: Option, - /// Фио преподавателя + /// Full name of the teacher. pub teacher: String, } #[derive(Clone, Hash, Debug, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct Lesson { - /// Тип занятия + /// Type. #[serde(rename = "type")] pub lesson_type: LessonType, - /// Индексы пар, если присутствуют + /// Lesson indexes, if present. pub default_range: Option<[u8; 2]>, - /// Название занятия + /// Name. pub name: Option, - /// Начало и конец занятия + /// The beginning and end. pub time: LessonTime, - /// Список подгрупп + /// List of subgroups. #[serde(rename = "subGroups")] pub subgroups: Option>, - /// Группа, если это расписание для преподавателей + /// Group name, if this is a schedule for teachers. pub group: Option, } #[derive(Clone, Hash, Debug, Serialize, Deserialize, ToSchema)] pub struct Day { - /// День недели + /// Day of the week. pub name: String, - /// Адрес другого корпуса + /// Address of another corps. pub street: Option, - /// Дата + /// Date. pub date: DateTime, - /// Список пар в этот день + /// List of lessons on this day. pub lessons: Vec, } #[derive(Clone, Hash, Debug, Serialize, Deserialize, ToSchema)] pub struct ScheduleEntry { - /// Название группы или ФИО преподавателя + /// The name of the group or name of the teacher. pub name: String, - /// Список из шести дней + /// List of six days. pub days: Vec, } #[derive(Clone)] pub struct ParseResult { - /// Список групп + /// List of groups. pub groups: HashMap, - /// Список преподавателей + /// List of teachers. pub teachers: HashMap, } #[derive(Debug, Display, Clone, ToSchema)] pub enum ParseError { - /// Ошибки связанные с чтением XLS файла. + /// Errors related to reading XLS file. #[display("{}: Failed to read XLS file.", "_0")] #[schema(value_type = String)] BadXLS(Arc), - /// Не найдено ни одного листа + /// Not a single sheet was found. #[display("No work sheets found.")] NoWorkSheets, - /// Отсутствуют данные об границах листа + /// There are no data on the boundaries of the sheet. #[display("There is no data on work sheet boundaries.")] UnknownWorkSheetRange, - /// Не удалось прочитать начало и конец пары из строки + /// Failed to read the beginning and end of the lesson from the line #[display("Failed to read lesson start and end times from string.")] GlobalTime, - /// Не найдены начало и конец соответствующее паре + /// Not found the beginning and the end corresponding to the lesson. #[display("No start and end times matching the lesson was found.")] LessonTimeNotFound, - /// Не удалось прочитать индекс подгруппы + /// Failed to read the subgroup index. #[display("Failed to read subgroup index.")] SubgroupIndexParsingFailed, } diff --git a/src/routes/auth/sign_in.rs b/src/routes/auth/sign_in.rs index aaf04ef..2a16e46 100644 --- a/src/routes/auth/sign_in.rs +++ b/src/routes/auth/sign_in.rs @@ -86,11 +86,11 @@ mod schema { #[derive(Deserialize, Serialize, ToSchema)] #[schema(as = SignIn::Request)] pub struct Request { - /// Имя пользователя + /// User name. #[schema(examples("n08i40k"))] pub username: String, - /// Пароль + /// Password. pub password: String, } @@ -102,7 +102,7 @@ mod schema { #[serde(rename_all = "camelCase")] #[schema(as = SignInVk::Request)] pub struct Request { - /// Токен VK ID + /// VK ID token. pub access_token: String, } } @@ -114,21 +114,21 @@ mod schema { #[schema(as = SignIn::ErrorCode)] #[status_code = "actix_web::http::StatusCode::NOT_ACCEPTABLE"] pub enum ErrorCode { - /// Некорректное имя пользователя или пароль + /// Incorrect username or password. IncorrectCredentials, - /// Недействительный токен VK ID + /// Invalid VK ID token. InvalidVkAccessToken, } /// Internal - /// Тип авторизации + /// Type of authorization. pub enum SignInData { - /// Имя пользователя и пароль + /// User and password name and password. Default(Request), - /// Идентификатор привязанного аккаунта VK + /// Identifier of the attached account VK. Vk(i32), } } diff --git a/src/routes/auth/sign_up.rs b/src/routes/auth/sign_up.rs index ce5c2c5..b27d411 100644 --- a/src/routes/auth/sign_up.rs +++ b/src/routes/auth/sign_up.rs @@ -50,10 +50,7 @@ async fn sign_up_combined( (status = NOT_ACCEPTABLE, body = ResponseError) ))] #[post("/sign-up")] -pub async fn sign_up( - data_json: Json, - app_state: web::Data, -) -> ServiceResponse { +pub async fn sign_up(data_json: Json, app_state: web::Data) -> ServiceResponse { let data = data_json.into_inner(); sign_up_combined( @@ -124,21 +121,21 @@ mod schema { #[derive(Serialize, Deserialize, utoipa::ToSchema)] #[schema(as = SignUp::Request)] pub struct Request { - /// Имя пользователя + /// User name. #[schema(examples("n08i40k"))] pub username: String, - /// Пароль + /// Password. pub password: String, - /// Группа + /// Group. #[schema(examples("ИС-214/23"))] pub group: String, - /// Роль + /// Role. pub role: UserRole, - /// Версия установленного приложения Polytechnic+ + /// Version of the installed Polytechnic+ application. #[schema(examples("3.0.0"))] pub version: String, } @@ -151,21 +148,21 @@ mod schema { #[serde(rename_all = "camelCase")] #[schema(as = SignUpVk::Request)] pub struct Request { - /// Токен VK ID + /// VK ID token. pub access_token: String, - /// Имя пользователя + /// User name. #[schema(examples("n08i40k"))] pub username: String, - /// Группа + /// Group. #[schema(examples("ИС-214/23"))] pub group: String, - /// Роль + /// Role. pub role: UserRole, - /// Версия установленного приложения Polytechnic+ + /// Version of the installed Polytechnic+ application. #[schema(examples("3.0.0"))] pub version: String, } @@ -178,44 +175,44 @@ mod schema { #[schema(as = SignUp::ErrorCode)] #[status_code = "actix_web::http::StatusCode::NOT_ACCEPTABLE"] pub enum ErrorCode { - /// Передана роль ADMIN + /// Conveyed the role of Admin. DisallowedRole, - /// Неизвестное название группы + /// Unknown name of the group. InvalidGroupName, - /// Пользователь с таким именем уже зарегистрирован + /// User with this name is already registered. UsernameAlreadyExists, - /// Недействительный токен VK ID + /// Invalid VK ID token. InvalidVkAccessToken, - /// Пользователь с таким аккаунтом VK уже зарегистрирован + /// User with such an account VK is already registered. VkAlreadyExists, } /// Internal - /// Данные для регистрации + /// Data for registration. pub struct SignUpData { - /// Имя пользователя + /// User name. pub username: String, - /// Пароль + /// Password. /// - /// Должен присутствовать даже если регистрация происходит с помощью токена VK ID + /// Should be present even if registration occurs using the VK ID token. pub password: String, - /// Идентификатор аккаунта VK + /// Account identifier VK. pub vk_id: Option, - /// Группа + /// Group. pub group: String, - /// Роль + /// Role. pub role: UserRole, - /// Версия установленного приложения Polytechnic+ + /// Version of the installed Polytechnic+ application. pub version: String, } @@ -302,10 +299,7 @@ mod tests { test_env(); let app_state = static_app_state().await; - driver::users::delete_by_username( - &app_state, - &"test::sign_up_multiple".to_string(), - ); + driver::users::delete_by_username(&app_state, &"test::sign_up_multiple".to_string()); let create = sign_up_client(SignUpPartial { username: "test::sign_up_multiple".to_string(), diff --git a/src/routes/schedule/group.rs b/src/routes/schedule/group.rs index f40d2b3..dc6081b 100644 --- a/src/routes/schedule/group.rs +++ b/src/routes/schedule/group.rs @@ -25,10 +25,7 @@ use actix_web::{get, web}; ), ))] #[get("/group")] -pub async fn group( - user: SyncExtractor, - app_state: web::Data, -) -> ServiceResponse { +pub async fn group(user: SyncExtractor, app_state: web::Data) -> ServiceResponse { // Prevent thread lock let schedule_lock = app_state.schedule.lock().unwrap(); @@ -55,22 +52,22 @@ mod schema { #[schema(as = GetGroup::Response)] #[serde(rename_all = "camelCase")] pub struct Response { - /// Расписание группы + /// Group schedule. pub group: ScheduleEntry, - - /// Устаревшая переменная - /// - /// По умолчанию возвращается пустой список + + /// ## Outdated variable. + /// + /// By default, an empty list is returned. #[deprecated = "Will be removed in future versions"] pub updated: Vec, - /// Устаревшая переменная + /// ## Outdated variable. /// - /// По умолчанию начальная дата по Unix + /// By default, the initial date for unix. #[deprecated = "Will be removed in future versions"] pub updated_at: DateTime, } - + #[allow(deprecated)] impl From for Response { fn from(group: ScheduleEntry) -> Self { @@ -86,12 +83,12 @@ mod schema { #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[schema(as = GroupSchedule::ErrorCode)] pub enum ErrorCode { - /// Расписания ещё не получены + /// Schedules have not yet been parsed. #[status_code = "actix_web::http::StatusCode::SERVICE_UNAVAILABLE"] #[display("Schedule not parsed yet.")] NoSchedule, - /// Группа не найдена + /// Group not found. #[status_code = "actix_web::http::StatusCode::NOT_FOUND"] #[display("Required group not found.")] NotFound, diff --git a/src/routes/schedule/group_names.rs b/src/routes/schedule/group_names.rs index dbf7520..0aa803d 100644 --- a/src/routes/schedule/group_names.rs +++ b/src/routes/schedule/group_names.rs @@ -35,7 +35,7 @@ mod schema { #[derive(Serialize, ToSchema)] #[schema(as = GetGroupNames::Response)] pub struct Response { - /// Список названий групп отсортированный в алфавитном порядке + /// List of group names sorted in alphabetical order. #[schema(examples(json!(["ИС-214/23"])))] pub names: Vec, } diff --git a/src/routes/schedule/schema.rs b/src/routes/schedule/schema.rs index 6259dfa..56158c2 100644 --- a/src/routes/schedule/schema.rs +++ b/src/routes/schedule/schema.rs @@ -8,23 +8,23 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use utoipa::ToSchema; -/// Ответ от сервера с расписаниями +/// Response from schedule server. #[derive(Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ScheduleView { - /// ETag расписания на сервере политехникума + /// ETag schedules on polytechnic server. etag: String, - - /// Дата обновления расписания на сайте политехникума + + /// Schedule update date on polytechnic website. uploaded_at: DateTime, - - /// Дата последнего скачивания расписания с сервера политехникума + + /// Date last downloaded from the Polytechnic server. downloaded_at: DateTime, - - /// Расписание групп + + /// Groups schedule. groups: HashMap, - - /// Расписание преподавателей + + /// Teachers schedule. teachers: HashMap, } @@ -33,7 +33,7 @@ pub struct ScheduleView { #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[schema(as = ScheduleShared::ErrorCode)] pub enum ErrorCode { - /// Расписания ещё не получены + /// Schedules not yet parsed. #[display("Schedule not parsed yet.")] NoSchedule, } @@ -56,22 +56,22 @@ impl TryFrom<&web::Data> for ScheduleView { } } -/// Статус кешированного расписаний +/// Cached schedule status. #[derive(Serialize, Deserialize, ToSchema, ResponderJson)] #[serde(rename_all = "camelCase")] pub struct CacheStatus { - /// Хеш расписаний + /// Schedule hash. pub cache_hash: String, - - /// Требуется ли обновить ссылку на расписание + + /// Whether the schedule reference needs to be updated. pub cache_update_required: bool, - - /// Дата последнего обновления кеша + + /// Last cache update date. pub last_cache_update: i64, - - /// Дата обновления кешированного расписания - /// - /// Определяется сервером политехникума + + /// Cached schedule update date. + /// + /// Determined by the polytechnic's server. pub last_schedule_update: i64, } diff --git a/src/routes/schedule/teacher.rs b/src/routes/schedule/teacher.rs index 705b958..318d5e6 100644 --- a/src/routes/schedule/teacher.rs +++ b/src/routes/schedule/teacher.rs @@ -53,18 +53,18 @@ mod schema { #[schema(as = GetTeacher::Response)] #[serde(rename_all = "camelCase")] pub struct Response { - /// Расписание преподавателя + /// Teacher's schedule. pub teacher: ScheduleEntry, - /// Устаревшая переменная + /// ## Deprecated variable. /// - /// По умолчанию возвращается пустой список + /// By default, an empty list is returned. #[deprecated = "Will be removed in future versions"] pub updated: Vec, - /// Устаревшая переменная + /// ## Deprecated variable. /// - /// По умолчанию начальная дата по Unix + /// Defaults to the Unix start date. #[deprecated = "Will be removed in future versions"] pub updated_at: DateTime, } @@ -84,12 +84,12 @@ mod schema { #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[schema(as = TeacherSchedule::ErrorCode)] pub enum ErrorCode { - /// Расписания ещё не получены + /// Schedules have not yet been parsed. #[status_code = "actix_web::http::StatusCode::SERVICE_UNAVAILABLE"] #[display("Schedule not parsed yet.")] NoSchedule, - /// Преподаватель не найден + /// Teacher not found. #[status_code = "actix_web::http::StatusCode::NOT_FOUND"] #[display("Required teacher not found.")] NotFound, diff --git a/src/routes/schedule/teacher_names.rs b/src/routes/schedule/teacher_names.rs index 788c954..e88d238 100644 --- a/src/routes/schedule/teacher_names.rs +++ b/src/routes/schedule/teacher_names.rs @@ -35,7 +35,7 @@ mod schema { #[derive(Serialize, ToSchema)] #[schema(as = GetTeacherNames::Response)] pub struct Response { - /// Список имён преподавателей отсортированный в алфавитном порядке + /// List of teacher names sorted alphabetically. #[schema(examples(json!(["Хомченко Н.Е."])))] pub names: Vec, } diff --git a/src/routes/schedule/update_download_url.rs b/src/routes/schedule/update_download_url.rs index 6c4e7e3..0a0e039 100644 --- a/src/routes/schedule/update_download_url.rs +++ b/src/routes/schedule/update_download_url.rs @@ -84,7 +84,7 @@ mod schema { #[derive(Serialize, Deserialize, ToSchema)] pub struct Request { - /// Ссылка на расписание + /// Schedule link. pub url: String, } @@ -92,25 +92,26 @@ mod schema { #[status_code = "actix_web::http::StatusCode::NOT_ACCEPTABLE"] #[schema(as = SetDownloadUrl::ErrorCode)] pub enum ErrorCode { - /// Передана ссылка с хостом отличающимся от politehnikum-eng.ru + /// Transferred link with host different from politehnikum-eng.ru. #[display("URL with unknown host provided. Provide url with politehnikum-eng.ru host.")] NonWhitelistedHost, - /// Не удалось получить мета-данные файла + /// Failed to retrieve file metadata. #[display("Unable to retrieve metadata from the specified URL.")] FetchFailed, - /// Не удалось скачать файл + /// Failed to download the file. #[display("Unable to retrieve data from the specified URL.")] DownloadFailed, - /// Ссылка ведёт на устаревшее расписание + /// The link leads to an outdated schedule. /// - /// Под устаревшим расписанием подразумевается расписание, которое было опубликовано раньше, чем уже имеется на данный момент + /// An outdated schedule refers to a schedule that was published earlier + /// than is currently available. #[display("The schedule is older than it already is.")] OutdatedSchedule, - /// Не удалось преобразовать расписание + /// Failed to parse the schedule. #[display("{}", "_0.display()")] InvalidSchedule(ParseError), } diff --git a/src/routes/schema.rs b/src/routes/schema.rs index 15e9e00..1854720 100644 --- a/src/routes/schema.rs +++ b/src/routes/schema.rs @@ -112,7 +112,8 @@ pub mod user { use crate::database::models::{User, UserRole}; use actix_macros::ResponderJson; use serde::Serialize; - + + //noinspection SpellCheckingInspection /// Используется для скрытия чувствительных полей, таких как хеш пароля или FCM #[derive(Serialize, utoipa::ToSchema, ResponderJson)] #[serde(rename_all = "camelCase")] @@ -132,7 +133,7 @@ pub mod user { /// Роль role: UserRole, - /// Идентификатор прявязанного аккаунта VK + /// Идентификатор привязанного аккаунта VK #[schema(examples(498094647, json!(null)))] vk_id: Option, diff --git a/src/routes/users/change_group.rs b/src/routes/users/change_group.rs index 6ab4f25..f114d69 100644 --- a/src/routes/users/change_group.rs +++ b/src/routes/users/change_group.rs @@ -54,7 +54,7 @@ mod schema { #[derive(Serialize, Deserialize, ToSchema)] #[schema(as = ChangeGroup::Request)] pub struct Request { - /// Название группы. + /// Group name. pub group: String, } @@ -63,21 +63,21 @@ mod schema { #[schema(as = ChangeGroup::ErrorCode)] #[status_code = "actix_web::http::StatusCode::CONFLICT"] pub enum ErrorCode { - /// Расписания ещё не получены. + /// Schedules have not yet been received. #[display("Schedule not parsed yet.")] #[status_code = "actix_web::http::StatusCode::SERVICE_UNAVAILABLE"] NoSchedule, - /// Передано то же название группы, что есть на данный момент. + /// Passed the same group name that is currently there. #[display("Passed the same group name as it is at the moment.")] SameGroup, - /// Требуемая группа не существует. + /// The required group does not exist. #[display("The required group does not exist.")] #[status_code = "actix_web::http::StatusCode::NOT_FOUND"] NotFound, - /// Ошибка на стороне сервера. + /// Server-side error. #[display("Internal server error.")] #[status_code = "actix_web::http::StatusCode::INTERNAL_SERVER_ERROR"] InternalServerError, diff --git a/src/routes/users/change_username.rs b/src/routes/users/change_username.rs index e817f2e..adb217d 100644 --- a/src/routes/users/change_username.rs +++ b/src/routes/users/change_username.rs @@ -45,7 +45,7 @@ mod schema { #[derive(Serialize, Deserialize, ToSchema)] #[schema(as = ChangeUsername::Request)] pub struct Request { - /// Новое имя. + /// User name. pub username: String, } @@ -54,15 +54,15 @@ mod schema { #[schema(as = ChangeUsername::ErrorCode)] #[status_code = "actix_web::http::StatusCode::CONFLICT"] pub enum ErrorCode { - /// Передано то же имя, что есть на данный момент. + /// The same name that is currently present is passed. #[display("Passed the same name as it is at the moment.")] SameUsername, - /// Пользователь с таким именем уже существует. + /// A user with this name already exists. #[display("A user with this name already exists.")] AlreadyExists, - /// Ошибка на стороне сервера. + /// Server-side error. #[display("Internal server error.")] #[status_code = "actix_web::http::StatusCode::INTERNAL_SERVER_ERROR"] InternalServerError, diff --git a/src/routes/vk_id/oauth.rs b/src/routes/vk_id/oauth.rs index d50618a..84c7af2 100644 --- a/src/routes/vk_id/oauth.rs +++ b/src/routes/vk_id/oauth.rs @@ -84,13 +84,13 @@ mod schema { #[serde(rename_all = "camelCase")] #[schema(as = VkIdOAuth::Request)] pub struct Request { - /// Код подтверждения authorization_code + /// Код подтверждения authorization_code. pub code: String, - /// Параметр для защиты передаваемых данных + /// Parameter to protect transmitted data. pub code_verifier: String, - /// Идентификатор устройства + /// Device ID. pub device_id: String, } @@ -98,7 +98,7 @@ mod schema { #[serde(rename_all = "camelCase")] #[schema(as = VkIdOAuth::Response)] pub struct Response { - /// ID токен + /// ID token. pub access_token: String, } @@ -107,7 +107,7 @@ mod schema { #[schema(as = VkIdOAuth::ErrorCode)] #[status_code = "actix_web::http::StatusCode::NOT_ACCEPTABLE"] pub enum ErrorCode { - /// Сервер VK вернул ошибку + /// VK server returned an error. #[display("VK server returned an error")] VkIdError, } diff --git a/src/utility/error.rs b/src/utility/error.rs index aac46fb..d1a7535 100644 --- a/src/utility/error.rs +++ b/src/utility/error.rs @@ -2,7 +2,7 @@ use std::fmt::{Write}; use std::fmt::Display; use serde::{Deserialize, Serialize}; -/// Ответ от сервера при ошибках внутри Middleware +/// Server response to errors within Middleware. #[derive(Serialize, Deserialize)] pub struct ResponseErrorMessage { code: T, diff --git a/src/utility/hasher.rs b/src/utility/hasher.rs index 83f73da..3568f7b 100644 --- a/src/utility/hasher.rs +++ b/src/utility/hasher.rs @@ -1,7 +1,7 @@ use sha1::Digest; use std::hash::Hasher; -/// Хешер возвращающий хеш из алгоритма реализующего Digest +/// Hesher returning hash from the algorithm implementing Digest pub struct DigestHasher { digest: D, } @@ -10,7 +10,7 @@ impl DigestHasher where D: Digest, { - /// Получение хеша + /// Obtain hash. pub fn finalize(self) -> String { hex::encode(self.digest.finalize().0) } @@ -20,14 +20,14 @@ impl From for DigestHasher where D: Digest, { - /// Создания хешера из алгоритма реализующего Digest + /// Creating a hash from an algorithm implementing Digest. fn from(digest: D) -> Self { DigestHasher { digest } } } impl Hasher for DigestHasher { - /// Заглушка для предотвращения вызова стандартного результата Hasher + /// Stopper to prevent calling the standard Hasher result. fn finish(&self) -> u64 { unimplemented!("Do not call finish()"); } diff --git a/src/utility/jwt.rs b/src/utility/jwt.rs index 0e247a5..bd82254 100644 --- a/src/utility/jwt.rs +++ b/src/utility/jwt.rs @@ -9,31 +9,31 @@ use std::env; use std::mem::discriminant; use std::sync::LazyLock; -/// Ключ для верификации токена +/// Key for token verification. static DECODING_KEY: LazyLock = LazyLock::new(|| { let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set"); DecodingKey::from_secret(secret.as_bytes()) }); -/// Ключ для создания подписанного токена +/// Key for creating a signed token. static ENCODING_KEY: LazyLock = LazyLock::new(|| { let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set"); EncodingKey::from_secret(secret.as_bytes()) }); -/// Ошибки верификации токена +/// Token verification errors. #[allow(dead_code)] #[derive(Debug)] pub enum Error { - /// Токен имеет другую подпись + /// The token has a different signature. InvalidSignature, - - /// Ошибка чтения токена + + /// Token reading error. InvalidToken(ErrorKind), - - /// Токен просрочен + + /// Token expired. Expired, } @@ -43,26 +43,26 @@ impl PartialEq for Error { } } -/// Данные, которые хранит в себе токен +/// The data the token holds. #[serde_as] #[derive(Debug, Serialize, Deserialize)] struct Claims { - /// UUID аккаунта пользователя + /// User account UUID. id: String, - - /// Дата создания токена + + /// Token creation date. #[serde_as(as = "DisplayFromStr")] iat: u64, - - /// Дата окончания действия токена + + /// Token expiry date. #[serde_as(as = "DisplayFromStr")] exp: u64, } -/// Алгоритм подписи токенов +/// Token signing algorithm. pub(crate) const DEFAULT_ALGORITHM: Algorithm = Algorithm::HS256; -/// Проверка токена и извлечение из него UUID аккаунта пользователя +/// Checking the token and extracting the UUID of the user account from it. pub fn verify_and_decode(token: &String) -> Result { let mut validation = Validation::new(DEFAULT_ALGORITHM); @@ -87,7 +87,7 @@ pub fn verify_and_decode(token: &String) -> Result { } } -/// Создание токена пользователя +/// Creating a user token. pub fn encode(id: &String) -> String { let header = Header { typ: Some(String::from("JWT")), @@ -132,6 +132,7 @@ mod tests { ); } + //noinspection SpellCheckingInspection #[test] fn test_decode_invalid_signature() { test_env(); @@ -143,6 +144,7 @@ mod tests { assert_eq!(result.err().unwrap(), Error::InvalidSignature); } + //noinspection SpellCheckingInspection #[test] fn test_decode_expired() { test_env(); @@ -154,6 +156,7 @@ mod tests { assert_eq!(result.err().unwrap(), Error::Expired); } + //noinspection SpellCheckingInspection #[test] fn test_decode_ok() { test_env(); diff --git a/src/utility/mutex.rs b/src/utility/mutex.rs index e082cb2..126cb3c 100644 --- a/src/utility/mutex.rs +++ b/src/utility/mutex.rs @@ -5,6 +5,22 @@ pub trait MutexScope where ScopeFn: FnOnce(&mut T) -> ScopeFnOutput, { + /// Replaces manually creating a mutex lock to perform operations on the data it manages. + /// + /// # Arguments + /// + /// * `f`: Function (mostly lambda) to which a reference to the mutable object stored in the mutex will be passed. + /// + /// returns: Return value of `f` function. + /// + /// # Examples + /// + /// ``` + /// let mtx: Mutex = Mutex::new(10); + /// + /// let res = mtx.scope(|x| { *x = *x * 2; *x }); + /// assert_eq!(res, *mtx.lock().unwrap()); + /// ``` fn scope(&self, f: ScopeFn) -> ScopeFnOutput; } @@ -21,6 +37,24 @@ where } pub trait MutexScopeAsync { + /// ## Asynchronous variant of [MutexScope::scope][MutexScope::scope]. + /// + /// Replaces manually creating a mutex lock to perform operations on the data it manages. + /// + /// # Arguments + /// + /// * `f`: Asynchronous function (mostly lambda) to which a reference to the mutable object stored in the mutex will be passed. + /// + /// returns: Return value of `f` function. + /// + /// # Examples + /// + /// ``` + /// let mtx: Mutex = Mutex::new(10); + /// + /// let res = mtx.async_scope(async |x| { *x = *x * 2; *x }).await; + /// assert_eq!(res, *mtx.lock().unwrap()); + /// ``` async fn async_scope<'a, F, FnFut, FnOut>(&'a self, f: F) -> FnOut where FnFut: Future, @@ -40,4 +74,4 @@ impl MutexScopeAsync for Mutex { let ptr: &'a mut T = unsafe { &mut *(guard.deref_mut() as *mut _) }; f(ptr).await } -} \ No newline at end of file +} diff --git a/src/xls_downloader/interface.rs b/src/xls_downloader/interface.rs index 9263880..ef7d197 100644 --- a/src/xls_downloader/interface.rs +++ b/src/xls_downloader/interface.rs @@ -1,41 +1,41 @@ use chrono::{DateTime, Utc}; -/// Ошибки получения данных XLS +/// XLS data retrieval errors. #[derive(PartialEq, Debug)] pub enum FetchError { - /// Не установлена ссылка на файл + /// File url is not set. NoUrlProvided, - /// Неизвестная ошибка + /// Unknown error. Unknown, - /// Сервер вернул статус код отличающийся от 200 + /// Server returned a status code different from 200. BadStatusCode, - /// Ссылка ведёт на файл другого типа + /// The url leads to a file of a different type. BadContentType, - /// Сервер не вернул ожидаемые заголовки + /// Server doesn't return expected headers. BadHeaders, } -/// Результат получения данных XLS +/// Result of XLS data retrieval. pub struct FetchOk { - /// ETag объекта + /// ETag object. pub etag: String, - /// Дата загрузки файла + /// File upload date. pub uploaded_at: DateTime, - /// Дата получения данных + /// Date data received. pub requested_at: DateTime, - /// Данные файла + /// File data. pub data: Option>, } impl FetchOk { - /// Результат без контента файла + /// Result without file content. pub fn head(etag: String, uploaded_at: DateTime) -> Self { FetchOk { etag, @@ -45,7 +45,7 @@ impl FetchOk { } } - /// Полный результат + /// Full result. pub fn get(etag: String, uploaded_at: DateTime, data: Vec) -> Self { FetchOk { etag, @@ -59,9 +59,9 @@ impl FetchOk { pub type FetchResult = Result; pub trait XLSDownloader { - /// Получение данных о файле, и, опционально, его контент + /// Get data about the file, and optionally its content. async fn fetch(&self, head: bool) -> FetchResult; - /// Установка ссылки на файл + /// Setting the file link. async fn set_url(&mut self, url: String) -> FetchResult; }