From b418cbd54163c3316d34c1bf9fbc96a16bcde0c7 Mon Sep 17 00:00:00 2001 From: Bulat Kurbanov Date: Fri, 1 Sep 2023 00:40:59 +0200 Subject: [PATCH] Refactor annotation module --- .../modules/annotations/callback_data.rs | 61 ++++++++ .../modules/annotations/commands.rs | 37 +++++ .../modules/annotations/errors.rs | 18 +++ .../modules/annotations/formater.rs | 37 +++++ .../{annotations.rs => annotations/mod.rs} | 141 ++---------------- .../approved_bot/services/book_cache/mod.rs | 1 + 6 files changed, 165 insertions(+), 130 deletions(-) create mode 100644 src/bots/approved_bot/modules/annotations/callback_data.rs create mode 100644 src/bots/approved_bot/modules/annotations/commands.rs create mode 100644 src/bots/approved_bot/modules/annotations/errors.rs create mode 100644 src/bots/approved_bot/modules/annotations/formater.rs rename src/bots/approved_bot/modules/{annotations.rs => annotations/mod.rs} (62%) diff --git a/src/bots/approved_bot/modules/annotations/callback_data.rs b/src/bots/approved_bot/modules/annotations/callback_data.rs new file mode 100644 index 0000000..809cb6e --- /dev/null +++ b/src/bots/approved_bot/modules/annotations/callback_data.rs @@ -0,0 +1,61 @@ +use std::str::FromStr; + +use regex::Regex; + +use crate::bots::approved_bot::modules::utils::GetPaginationCallbackData; + + +#[derive(Debug, Clone)] +pub enum AnnotationCallbackData { + Book { id: u32, page: u32 }, + Author { id: u32, page: u32 }, +} + +impl FromStr for AnnotationCallbackData { + type Err = strum::ParseError; + + fn from_str(s: &str) -> Result { + let re = Regex::new(r"^(?Pa|b)_an_(?P\d+)_(?P\d+)$").unwrap(); + + let caps = re.captures(s); + let caps = match caps { + Some(v) => v, + None => return Err(strum::ParseError::VariantNotFound), + }; + + let annotation_type = &caps["an_type"]; + let id = caps["id"].parse::().unwrap(); + let page = caps["page"].parse::().unwrap(); + + match annotation_type { + "a" => Ok(AnnotationCallbackData::Author { id, page }), + "b" => Ok(AnnotationCallbackData::Book { id, page }), + _ => Err(strum::ParseError::VariantNotFound), + } + } +} + +impl ToString for AnnotationCallbackData { + fn to_string(&self) -> String { + match self { + AnnotationCallbackData::Book { id, page } => format!("b_an_{id}_{page}"), + AnnotationCallbackData::Author { id, page } => format!("a_an_{id}_{page}"), + } + } +} + +impl GetPaginationCallbackData for AnnotationCallbackData { + fn get_pagination_callback_data(&self, target_page: u32) -> String { + match self { + AnnotationCallbackData::Book { id, .. } => AnnotationCallbackData::Book { + id: *id, + page: target_page, + }, + AnnotationCallbackData::Author { id, .. } => AnnotationCallbackData::Author { + id: *id, + page: target_page, + }, + } + .to_string() + } +} diff --git a/src/bots/approved_bot/modules/annotations/commands.rs b/src/bots/approved_bot/modules/annotations/commands.rs new file mode 100644 index 0000000..b4ceceb --- /dev/null +++ b/src/bots/approved_bot/modules/annotations/commands.rs @@ -0,0 +1,37 @@ +use regex::Regex; + +use crate::bots::approved_bot::modules::utils::CommandParse; + + +#[derive(Debug, Clone)] +pub enum AnnotationCommand { + Book { id: u32 }, + Author { id: u32 }, +} + +impl CommandParse for AnnotationCommand { + fn parse(s: &str, bot_name: &str) -> Result { + let re = Regex::new(r"^/(?Pa|b)_an_(?P\d+)$") + .unwrap_or_else(|_| panic!("Can't create AnnotationCommand regexp!")); + + let full_bot_name = format!("@{bot_name}"); + let after_replace = s.replace(&full_bot_name, ""); + + let caps = re.captures(&after_replace); + let caps = match caps { + Some(v) => v, + None => return Err(strum::ParseError::VariantNotFound), + }; + + let annotation_type = &caps["an_type"]; + let id: u32 = caps["id"] + .parse() + .unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!")); + + match annotation_type { + "a" => Ok(AnnotationCommand::Author { id }), + "b" => Ok(AnnotationCommand::Book { id }), + _ => Err(strum::ParseError::VariantNotFound), + } + } +} diff --git a/src/bots/approved_bot/modules/annotations/errors.rs b/src/bots/approved_bot/modules/annotations/errors.rs new file mode 100644 index 0000000..dca7687 --- /dev/null +++ b/src/bots/approved_bot/modules/annotations/errors.rs @@ -0,0 +1,18 @@ +use std::fmt; + +use super::commands::AnnotationCommand; + + +#[derive(Debug)] +pub struct AnnotationError { + pub command: AnnotationCommand, + pub text: String +} + +impl fmt::Display for AnnotationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl std::error::Error for AnnotationError {} \ No newline at end of file diff --git a/src/bots/approved_bot/modules/annotations/formater.rs b/src/bots/approved_bot/modules/annotations/formater.rs new file mode 100644 index 0000000..7960d2c --- /dev/null +++ b/src/bots/approved_bot/modules/annotations/formater.rs @@ -0,0 +1,37 @@ +use crate::bots::approved_bot::services::book_library::types::{AuthorAnnotation, BookAnnotation}; + +pub trait AnnotationFormat { + fn get_file(&self) -> Option<&String>; + fn get_text(&self) -> &str; + + fn is_normal_text(&self) -> bool; +} + +impl AnnotationFormat for BookAnnotation { + fn get_file(&self) -> Option<&String> { + self.file.as_ref() + } + + fn get_text(&self) -> &str { + self.text.as_str() + } + + fn is_normal_text(&self) -> bool { + !self.text.replace(['\n', ' '], "").is_empty() + } +} + + +impl AnnotationFormat for AuthorAnnotation { + fn get_file(&self) -> Option<&String> { + self.file.as_ref() + } + + fn get_text(&self) -> &str { + self.text.as_str() + } + + fn is_normal_text(&self) -> bool { + !self.text.replace(['\n', ' '], "").is_empty() + } +} diff --git a/src/bots/approved_bot/modules/annotations.rs b/src/bots/approved_bot/modules/annotations/mod.rs similarity index 62% rename from src/bots/approved_bot/modules/annotations.rs rename to src/bots/approved_bot/modules/annotations/mod.rs index cef4d65..8ce4116 100644 --- a/src/bots/approved_bot/modules/annotations.rs +++ b/src/bots/approved_bot/modules/annotations/mod.rs @@ -1,7 +1,12 @@ -use std::{convert::TryInto, str::FromStr}; +pub mod commands; +pub mod callback_data; +pub mod formater; +pub mod errors; + +use std::convert::TryInto; use futures::TryStreamExt; -use regex::Regex; + use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}}; use tokio_util::compat::FuturesAsyncReadCompatExt; @@ -10,137 +15,16 @@ use crate::bots::{ modules::utils::generic_get_pagination_keyboard, services::book_library::{ get_author_annotation, get_book_annotation, - types::{AuthorAnnotation, BookAnnotation}, }, tools::filter_callback_query, }, BotHandlerInternal, }; -use super::utils::{filter_command, CommandParse, GetPaginationCallbackData, split_text_to_chunks}; +use self::{commands::AnnotationCommand, formater::AnnotationFormat, callback_data::AnnotationCallbackData, errors::AnnotationError}; -#[derive(Clone)] -pub enum AnnotationCommand { - Book { id: u32 }, - Author { id: u32 }, -} +use super::utils::{filter_command, split_text_to_chunks}; -impl CommandParse for AnnotationCommand { - fn parse(s: &str, bot_name: &str) -> Result { - let re = Regex::new(r"^/(?Pa|b)_an_(?P\d+)$") - .unwrap_or_else(|_| panic!("Can't create AnnotationCommand regexp!")); - - let full_bot_name = format!("@{bot_name}"); - let after_replace = s.replace(&full_bot_name, ""); - - let caps = re.captures(&after_replace); - let caps = match caps { - Some(v) => v, - None => return Err(strum::ParseError::VariantNotFound), - }; - - let annotation_type = &caps["an_type"]; - let id: u32 = caps["id"] - .parse() - .unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!")); - - match annotation_type { - "a" => Ok(AnnotationCommand::Author { id }), - "b" => Ok(AnnotationCommand::Book { id }), - _ => Err(strum::ParseError::VariantNotFound), - } - } -} - -#[derive(Clone)] -pub enum AnnotationCallbackData { - Book { id: u32, page: u32 }, - Author { id: u32, page: u32 }, -} - -impl FromStr for AnnotationCallbackData { - type Err = strum::ParseError; - - fn from_str(s: &str) -> Result { - let re = Regex::new(r"^(?Pa|b)_an_(?P\d+)_(?P\d+)$").unwrap(); - - let caps = re.captures(s); - let caps = match caps { - Some(v) => v, - None => return Err(strum::ParseError::VariantNotFound), - }; - - let annotation_type = &caps["an_type"]; - let id = caps["id"].parse::().unwrap(); - let page = caps["page"].parse::().unwrap(); - - match annotation_type { - "a" => Ok(AnnotationCallbackData::Author { id, page }), - "b" => Ok(AnnotationCallbackData::Book { id, page }), - _ => Err(strum::ParseError::VariantNotFound), - } - } -} - -impl ToString for AnnotationCallbackData { - fn to_string(&self) -> String { - match self { - AnnotationCallbackData::Book { id, page } => format!("b_an_{id}_{page}"), - AnnotationCallbackData::Author { id, page } => format!("a_an_{id}_{page}"), - } - } -} - -pub trait AnnotationFormat { - fn get_file(&self) -> Option<&String>; - fn get_text(&self) -> &str; - - fn is_normal_text(&self) -> bool; -} - -impl AnnotationFormat for BookAnnotation { - fn get_file(&self) -> Option<&String> { - self.file.as_ref() - } - - fn get_text(&self) -> &str { - self.text.as_str() - } - - fn is_normal_text(&self) -> bool { - !self.text.replace(['\n', ' '], "").is_empty() - } -} - -impl GetPaginationCallbackData for AnnotationCallbackData { - fn get_pagination_callback_data(&self, target_page: u32) -> String { - match self { - AnnotationCallbackData::Book { id, .. } => AnnotationCallbackData::Book { - id: *id, - page: target_page, - }, - AnnotationCallbackData::Author { id, .. } => AnnotationCallbackData::Author { - id: *id, - page: target_page, - }, - } - .to_string() - } -} - -impl AnnotationFormat for AuthorAnnotation { - fn get_file(&self) -> Option<&String> { - self.file.as_ref() - } - - fn get_text(&self) -> &str { - self.text.as_str() - } - - fn is_normal_text(&self) -> bool { - !self.text.replace(['\n', ' '], "").is_empty() - } -} async fn download_image( file: &String, @@ -197,15 +81,12 @@ where }; if !annotation.is_normal_text() { - return Ok(()); // TODO: error message + return Err(Box::new(AnnotationError { command, text: annotation.get_text().to_string() })); } let annotation_text = annotation.get_text(); let chunked_text = split_text_to_chunks(annotation_text, 512); - let current_text = match chunked_text.get(0) { - Some(v) => v, - None => return Ok(()), // TODO: error message - }; + let current_text = chunked_text.get(0).unwrap(); let callback_data = match command { AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 }, diff --git a/src/bots/approved_bot/services/book_cache/mod.rs b/src/bots/approved_bot/services/book_cache/mod.rs index 6034fac..99de4bf 100644 --- a/src/bots/approved_bot/services/book_cache/mod.rs +++ b/src/bots/approved_bot/services/book_cache/mod.rs @@ -8,6 +8,7 @@ use self::types::{CachedMessage, DownloadFile, DownloadLink}; pub mod types; + #[derive(Debug, Clone)] struct DownloadError { status_code: StatusCode,