From c11780ba300776a674ae81a4be71d1b48015edd6 Mon Sep 17 00:00:00 2001 From: Bulat Kurbanov Date: Sat, 27 May 2023 22:27:45 +0200 Subject: [PATCH] Code refactor --- src/bots/approved_bot/modules/book.rs | 11 +- src/bots/approved_bot/modules/random.rs | 2 +- src/bots/approved_bot/modules/search.rs | 6 +- .../approved_bot/modules/update_history.rs | 4 +- .../services/book_library/formaters.rs | 367 +++++++----------- .../services/book_library/types.rs | 169 +++++--- 6 files changed, 252 insertions(+), 307 deletions(-) diff --git a/src/bots/approved_bot/modules/book.rs b/src/bots/approved_bot/modules/book.rs index b2af6be..e5138f6 100644 --- a/src/bots/approved_bot/modules/book.rs +++ b/src/bots/approved_bot/modules/book.rs @@ -165,10 +165,11 @@ where return Ok(()); }; - let formated_items = items_page.format_items(); let total_pages = items_page.total_pages; - let footer = format!("\n\nСтраница 1/{total_pages}"); + + let formated_items = items_page.format_items((4096 - footer.len()).try_into().unwrap()); + let message_text = format!("{formated_items}{footer}"); let callback_data = match command { @@ -256,11 +257,11 @@ where }; } - let formated_items = items_page.format_items(); - let total_pages = items_page.total_pages; - let footer = format!("\n\nСтраница {page}/{total_pages}"); + + let formated_items = items_page.format_items((4096 - footer.len()).try_into().unwrap()); + let message_text = format!("{formated_items}{footer}"); let keyboard = generic_get_pagination_keyboard(page, total_pages, callback_data, true); diff --git a/src/bots/approved_bot/modules/random.rs b/src/bots/approved_bot/modules/random.rs index 62479f3..d34d3bc 100644 --- a/src/bots/approved_bot/modules/random.rs +++ b/src/bots/approved_bot/modules/random.rs @@ -137,7 +137,7 @@ where }, }; - let item_message = item.format(); + let item_message = item.format(4096); bot.send_message(cq.from.id, item_message) .reply_markup(InlineKeyboardMarkup { diff --git a/src/bots/approved_bot/modules/search.rs b/src/bots/approved_bot/modules/search.rs index 0f44380..324e4a1 100644 --- a/src/bots/approved_bot/modules/search.rs +++ b/src/bots/approved_bot/modules/search.rs @@ -188,11 +188,11 @@ where }; } - let formated_items = items_page.format_items(); - let total_pages = items_page.total_pages; - let footer = format!("\n\nСтраница {page}/{total_pages}"); + + let formated_items = items_page.format_items((4096 - footer.len()).try_into().unwrap()); + let message_text = format!("{formated_items}{footer}"); let keyboard = generic_get_pagination_keyboard(page, total_pages, search_data, true); diff --git a/src/bots/approved_bot/modules/update_history.rs b/src/bots/approved_bot/modules/update_history.rs index a1ee1fa..b6fe182 100644 --- a/src/bots/approved_bot/modules/update_history.rs +++ b/src/bots/approved_bot/modules/update_history.rs @@ -173,12 +173,12 @@ async fn update_log_pagination_handler( ).await?; } - let formated_items = items_page.format_items(); - let page = update_callback_data.page; let total_pages = items_page.total_pages; let footer = format!("\n\nСтраница {page}/{total_pages}"); + let formated_items = items_page.format_items((4096 - footer.len()).try_into().unwrap()); + let message_text = format!("{header}{formated_items}{footer}"); let keyboard = generic_get_pagination_keyboard(page, total_pages, update_callback_data, true); diff --git a/src/bots/approved_bot/services/book_library/formaters.rs b/src/bots/approved_bot/services/book_library/formaters.rs index b4c1a30..e27c22e 100644 --- a/src/bots/approved_bot/services/book_library/formaters.rs +++ b/src/bots/approved_bot/services/book_library/formaters.rs @@ -2,97 +2,116 @@ use std::cmp::min; use crate::bots::approved_bot::modules::download::StartDownloadData; -use super::types::{Author, AuthorBook, Book, SearchBook, Sequence, Translator, TranslatorBook}; +use super::types::{Author, AuthorBook, Book, SearchBook, Sequence, Translator, TranslatorBook, BookAuthor, BookGenre, AsBook}; + +const NO_LIMIT: u32 = 4096; + pub trait Format { - fn format(&self) -> String; + fn format(&self, max_size: u32) -> String; } -impl Format for Book { - fn format(&self) -> String { - let book_title = { - let Book { title, lang, .. } = self; - format!("📖 {title} | {lang}\n") - }; - let pages_count = match self.pages { - Some(1) | None => "".to_string(), - Some(v) => format!("[ {v}с. ]\n\n"), - }; +pub trait FormatInline { + fn format_inline(&self) -> String; +} - let annotations = match self.annotation_exists { - true => { - let Book { id, .. } = self; - format!("📝 Аннотация: /b_an_{id}\n\n") - } - false => "".to_string(), - }; - let authors = match !self.authors.is_empty() { - true => { - let formated_authors = self - .authors - .clone() - .into_iter() - .map(|author| author.format_author()) - .collect::>() - .join("\n"); - format!("Авторы:\n{formated_authors}\n\n") - } - false => "".to_string(), - }; +impl FormatInline for BookAuthor { + fn format_inline(&self) -> String { + let BookAuthor { + id, + last_name, + first_name, + middle_name, + } = self; - let translators = match !self.translators.is_empty() { - true => { - let formated_translators = self - .translators - .clone() - .into_iter() - .map(|translator| translator.format_translator()) - .collect::>() - .join("\n"); - format!("Переводчики:\n{formated_translators}\n\n") - } - false => "".to_string(), - }; - - let sequences = match !self.sequences.is_empty() { - true => { - let formated_sequences: String = self - .sequences - .clone() - .into_iter() - .map(|sequence| sequence.format()) - .collect::>() - .join("\n"); - format!("Серии:\n{formated_sequences}\n\n") - } - false => "".to_string(), - }; - - let genres = match !self.genres.is_empty() { - true => { - let formated_genres: String = self - .genres - .clone() - .into_iter() - .map(|genre| genre.format()) - .collect::>() - .join("\n"); - format!("Жанры:\n{formated_genres}\n\n") - } - false => "".to_string(), - }; - - let download_command = (StartDownloadData { id: self.id }).to_string(); - let download_links = format!("Скачать:\n📥{download_command}"); - - format!("{book_title}{pages_count}{annotations}{authors}{translators}{sequences}{genres}{download_links}") + format!("👤 {last_name} {first_name} {middle_name} /a_{id}") } } + +impl FormatInline for Translator { + fn format_inline(&self) -> String { + let Translator { + id, + first_name, + last_name, + middle_name, + .. + } = self; + + format!("👤 {last_name} {first_name} {middle_name} /t_{id}") + } +} + + +fn format_authors(authors: Vec, count: usize) -> String { + match !authors.is_empty() { + true => { + let formated_authors = authors + .clone()[..min(count, authors.len())] + .into_iter() + .map(|author| author.format_inline()) + .collect::>() + .join("\n"); + format!("Авторы:\n{formated_authors}\n\n") + } + false => "".to_string(), + } +} + + +fn format_translators(translators: Vec, count: usize) -> String { + match !translators.is_empty() { + true => { + let formated_translators = translators + .clone()[..min(count, translators.len())] + .into_iter() + .map(|translator| translator.format_inline()) + .collect::>() + .join("\n"); + format!("Переводчики:\n{formated_translators}\n\n") + } + false => "".to_string(), + } +} + + +fn format_sequences(sequences: Vec, count: usize) -> String { + match !sequences.is_empty() { + true => { + let formated_sequences: String = sequences + .clone()[..min(count, sequences.len())] + .into_iter() + .map(|sequence| sequence.format(NO_LIMIT)) + .collect::>() + .join("\n"); + format!("Серии:\n{formated_sequences}\n\n") + } + false => "".to_string(), + } +} + + +fn format_genres(genres: Vec, count: usize) -> String { + match !genres.is_empty() { + true => { + let formated_genres: String = genres + .clone()[..min(count, genres.len())] + .into_iter() + .map(|genre| genre.format()) + .collect::>() + .join("\n"); + format!("Жанры:\n{formated_genres}\n\n") + } + false => "".to_string(), + } +} + + impl Format for Author { - fn format(&self) -> String { + fn format(&self, _max_size: u32) -> String { let Author { id, last_name, @@ -112,8 +131,9 @@ impl Format for Author { } } + impl Format for Sequence { - fn format(&self) -> String { + fn format(&self, _max_size: u32) -> String { let Sequence { id, name, .. } = self; let title = format!("📚 {name}"); @@ -123,76 +143,9 @@ impl Format for Sequence { } } -impl Format for SearchBook { - fn format(&self) -> String { - let book_title = { - let SearchBook { title, lang, .. } = self; - format!("📖 {title} | {lang}\n") - }; - - let annotations = match self.annotation_exists { - true => { - let SearchBook { id, .. } = self; - format!("📝 Аннотация: /b_an_{id}\n") - } - false => "".to_string(), - }; - - let authors = if !self.authors.is_empty() { - let formated_authors = self - .authors - .clone()[..min(5, self.authors.len())] - .iter() - .map(|author| author.format_author()) - .collect::>() - .join("\n"); - - let post_fix = if self.authors.len() > 5 { "\nи др." } else { "" }; - format!("Авторы:\n{formated_authors}{post_fix}\n") - } else { - "".to_string() - }; - - let sequences = match !self.sequences.is_empty() { - true => { - let formated_sequences: String = self - .sequences - .clone()[..min(5, self.sequences.len())] - .into_iter() - .map(|sequence| sequence.format()) - .collect::>() - .join("\n"); - - let post_fix = if self.sequences.len() > 5 { "\nи др." } else { "" }; - format!("Серии:\n{formated_sequences}{post_fix}\n") - } - false => "".to_string(), - }; - - let translators = if !self.translators.is_empty() { - let formated_translators = self - .translators - .clone()[..min(5, self.translators.len())] - .iter() - .map(|translator| translator.format_translator()) - .collect::>() - .join("\n"); - - let post_fix = if self.translators.len() > 5 { "\nи др." } else { "" }; - format!("Переводчики:\n{formated_translators}{post_fix}\n") - } else { - "".to_string() - }; - - let download_command = (StartDownloadData { id: self.id }).to_string(); - let download_links = format!("Скачать:\n📥{download_command}"); - - format!("{book_title}{annotations}{authors}{translators}{sequences}{download_links}") - } -} impl Format for Translator { - fn format(&self) -> String { + fn format(&self, _max_size: u32) -> String { let Translator { id, last_name, @@ -212,47 +165,23 @@ impl Format for Translator { } } -impl Format for AuthorBook { - fn format(&self) -> String { + +impl Format for Book { + fn format(&self, max_size: u32) -> String { let book_title = { - let AuthorBook { title, lang, .. } = self; + let Book { title, lang, .. } = self; format!("📖 {title} | {lang}\n") }; + let pages_count = match self.pages { + Some(1) | None => "".to_string(), + Some(v) => format!("[ {v}с. ]\n\n"), + }; + let annotations = match self.annotation_exists { true => { - let AuthorBook { id, .. } = self; - format!("📝 Аннотация: /b_an_{id}\n") - } - false => "".to_string(), - }; - - let translators = match !self.translators.is_empty() { - true => { - let formated_translators = self - .translators - .clone() - .into_iter() - .map(|translator| translator.format_translator()) - .collect::>() - .join("\n"); - format!("Переводчики:\n{formated_translators}\n") - } - false => "".to_string(), - }; - - let sequences = match !self.sequences.is_empty() { - true => { - let formated_sequences: String = self - .sequences - .clone()[..min(5, self.sequences.len())] - .into_iter() - .map(|sequence| sequence.format()) - .collect::>() - .join("\n"); - - let post_fix = if self.sequences.len() > 5 { "\nи др." } else { "" }; - format!("Серии:\n{formated_sequences}{post_fix}\n") + let Book { id, .. } = self; + format!("📝 Аннотация: /b_an_{id}\n\n") } false => "".to_string(), }; @@ -260,58 +189,32 @@ impl Format for AuthorBook { let download_command = (StartDownloadData { id: self.id }).to_string(); let download_links = format!("Скачать:\n📥{download_command}"); - format!("{book_title}{annotations}{translators}{sequences}{download_links}") + let authors = format_authors(self.authors.clone(), self.authors.len()); + let translators = format_translators(self.translators.clone(), self.translators.len()); + let sequences = format_sequences(self.sequences.clone(), self.sequences.len()); + let genres = format_genres(self.genres.clone(), self.genres.len()); + + format!("{book_title}{pages_count}{annotations}{authors}{translators}{sequences}{genres}{download_links}") } } + +impl Format for SearchBook { + fn format(&self, max_size: u32) -> String { + self.clone().as_book().format(max_size) + } +} + + +impl Format for AuthorBook { + fn format(&self, max_size: u32) -> String { + self.clone().as_book().format(max_size) + } +} + + impl Format for TranslatorBook { - fn format(&self) -> String { - let book_title = { - let TranslatorBook { title, lang, .. } = self; - format!("📖 {title} | {lang}\n") - }; - - let annotations = match self.annotation_exists { - true => { - let TranslatorBook { id, .. } = self; - format!("📝 Аннотация: /b_an_{id}\n") - } - false => "".to_string(), - }; - - let authors = match !self.authors.is_empty() { - true => { - let formated_authors = self - .authors - .clone() - .into_iter() - .map(|author| author.format_author()) - .collect::>() - .join("\n"); - format!("Авторы:\n{formated_authors}\n") - } - false => "".to_string(), - }; - - let sequences = match !self.sequences.is_empty() { - true => { - let formated_sequences: String = self - .sequences - .clone()[..min(5, self.sequences.len())] - .into_iter() - .map(|sequence| sequence.format()) - .collect::>() - .join("\n"); - - let post_fix = if self.sequences.len() > 5 { "\nи др." } else { "" }; - format!("Серии:\n{formated_sequences}{post_fix}\n") - } - false => "".to_string(), - }; - - let download_command = (StartDownloadData { id: self.id }).to_string(); - let download_links = format!("Скачать:\n📥{download_command}"); - - format!("{book_title}{annotations}{authors}{sequences}{download_links}") + fn format(&self, max_size: u32) -> String { + self.clone().as_book().format(max_size) } } diff --git a/src/bots/approved_bot/services/book_library/types.rs b/src/bots/approved_bot/services/book_library/types.rs index 8bb785b..7deb49d 100644 --- a/src/bots/approved_bot/services/book_library/types.rs +++ b/src/bots/approved_bot/services/book_library/types.rs @@ -2,36 +2,13 @@ use serde::Deserialize; use super::formaters::Format; + #[derive(Deserialize, Debug, Clone)] pub struct BookAuthor { - id: u32, - first_name: String, - last_name: String, - middle_name: String, -} - -impl BookAuthor { - pub fn format_author(&self) -> String { - let BookAuthor { - id, - last_name, - first_name, - middle_name, - } = self; - - format!("👤 {last_name} {first_name} {middle_name} /a_{id}") - } - - pub fn format_translator(&self) -> String { - let BookAuthor { - id, - first_name, - last_name, - middle_name, - } = self; - - format!("👤 {last_name} {first_name} {middle_name} /t_{id}") - } + pub id: u32, + pub first_name: String, + pub last_name: String, + pub middle_name: String, } #[derive(Deserialize, Debug, Clone)] @@ -52,25 +29,6 @@ pub struct Source { // name: String } -#[derive(Deserialize, Debug, Clone)] -pub struct Book { - pub id: u32, - pub title: String, - pub lang: String, - // file_type: String, - pub available_types: Vec, - // uploaded: String, - pub annotation_exists: bool, - pub authors: Vec, - pub translators: Vec, - pub sequences: Vec, - pub genres: Vec, - // source: Source, - // remote_id: u32, - // id_deleted: bool, - pub pages: Option, -} - #[derive(Deserialize, Debug, Clone)] pub struct Author { pub id: u32, @@ -118,30 +76,19 @@ impl Page where T: Format + Clone, { - pub fn format_items(&self) -> String { + pub fn format_items(&self, max_size: u32) -> String { + let items_count: u32 = self.items.len().try_into().unwrap(); + let item_size: u32 = max_size / items_count; + self.items .clone() .into_iter() - .map(|book| book.format()) + .map(|item| item.format(item_size)) .collect::>() .join("\n\n\n") } } -#[derive(Deserialize, Debug, Clone)] -pub struct SearchBook { - pub id: u32, - pub title: String, - pub lang: String, - // file_type: String, - pub available_types: Vec, - // uploaded: String, - pub annotation_exists: bool, - pub authors: Vec, - pub translators: Vec, - pub sequences: Vec, -} - #[derive(Deserialize, Debug, Clone)] pub struct BookAnnotation { pub id: u32, @@ -158,6 +105,66 @@ pub struct AuthorAnnotation { pub file: Option, } +pub trait AsBook { + fn as_book(self) -> T; +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Book { + pub id: u32, + pub title: String, + pub lang: String, + // file_type: String, + pub available_types: Vec, + // uploaded: String, + pub annotation_exists: bool, + pub authors: Vec, + pub translators: Vec, + pub sequences: Vec, + pub genres: Vec, + // source: Source, + // remote_id: u32, + // id_deleted: bool, + pub pages: Option, +} + +impl AsBook for Book { + fn as_book(self) -> Book { + self + } +} + +#[derive(Deserialize, Debug, Clone)] +pub struct SearchBook { + pub id: u32, + pub title: String, + pub lang: String, + // file_type: String, + pub available_types: Vec, + // uploaded: String, + pub annotation_exists: bool, + pub authors: Vec, + pub translators: Vec, + pub sequences: Vec, +} + +impl AsBook for SearchBook { + fn as_book(self) -> Book { + Book { + id: self.id, + title: self.title, + lang: self.lang, + available_types: self.available_types, + annotation_exists: self.annotation_exists, + authors: self.authors, + translators: self.translators, + sequences: self.sequences, + genres: vec![], + pages: None + } + } +} + #[derive(Deserialize, Debug, Clone)] pub struct AuthorBook { pub id: u32, @@ -167,10 +174,27 @@ pub struct AuthorBook { pub available_types: Vec, // uploaded: String, pub annotation_exists: bool, - pub translators: Vec, + pub translators: Vec, pub sequences: Vec, } +impl AsBook for AuthorBook { + fn as_book(self) -> Book { + Book { + id: self.id, + title: self.title, + lang: self.lang, + available_types: self.available_types, + annotation_exists: self.annotation_exists, + authors: vec![], + translators: self.translators, + sequences: self.sequences, + genres: vec![], + pages: None + } + } +} + #[derive(Deserialize, Debug, Clone)] pub struct TranslatorBook { pub id: u32, @@ -183,3 +207,20 @@ pub struct TranslatorBook { pub authors: Vec, pub sequences: Vec, } + +impl AsBook for TranslatorBook { + fn as_book(self) -> Book { + Book { + id: self.id, + title: self.title, + lang: self.lang, + available_types: self.available_types, + annotation_exists: self.annotation_exists, + authors: self.authors, + translators: vec![], + sequences: self.sequences, + genres: vec![], + pages: None + } + } +}