diff --git a/src/bots/approved_bot/modules/book.rs b/src/bots/approved_bot/modules/book.rs index e5138f6..593118a 100644 --- a/src/bots/approved_bot/modules/book.rs +++ b/src/bots/approved_bot/modules/book.rs @@ -168,7 +168,7 @@ where 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 formated_items = items_page.format_items(4096 - footer.len()); let message_text = format!("{formated_items}{footer}"); @@ -260,7 +260,7 @@ where 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 formated_items = items_page.format_items(4096 - footer.len()); let message_text = format!("{formated_items}{footer}"); diff --git a/src/bots/approved_bot/modules/random.rs b/src/bots/approved_bot/modules/random.rs index d34d3bc..29a19ee 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(4096); + let item_message = item.format(4096).result; 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 324e4a1..379a77d 100644 --- a/src/bots/approved_bot/modules/search.rs +++ b/src/bots/approved_bot/modules/search.rs @@ -191,7 +191,7 @@ where 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 formated_items = items_page.format_items(4096 - footer.len()); let message_text = format!("{formated_items}{footer}"); diff --git a/src/bots/approved_bot/modules/update_history.rs b/src/bots/approved_bot/modules/update_history.rs index b6fe182..0a8f1e7 100644 --- a/src/bots/approved_bot/modules/update_history.rs +++ b/src/bots/approved_bot/modules/update_history.rs @@ -177,7 +177,7 @@ async fn update_log_pagination_handler( 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 formated_items = items_page.format_items(4096 - footer.len()); let message_text = format!("{header}{formated_items}{footer}"); diff --git a/src/bots/approved_bot/services/book_library/formaters.rs b/src/bots/approved_bot/services/book_library/formaters.rs index 00d18a9..cbaeb1c 100644 --- a/src/bots/approved_bot/services/book_library/formaters.rs +++ b/src/bots/approved_bot/services/book_library/formaters.rs @@ -7,10 +7,18 @@ use super::types::{ TranslatorBook, }; -const NO_LIMIT: u32 = 4096; +const NO_LIMIT: usize = 4096; + +#[derive(Clone)] +pub struct FormatResult { + pub result: String, + + pub current_size: usize, + pub max_size: usize, +} pub trait Format { - fn format(&self, max_size: u32) -> String; + fn format(&self, max_size: usize) -> FormatResult; } pub trait FormatInline { @@ -51,8 +59,8 @@ fn format_authors(authors: Vec, count: usize) -> String { match !authors.is_empty() { true => { - let formated_authors = authors.clone()[..min(count, authors.len())] - .into_iter() + let formated_authors = authors[..min(count, authors.len())] + .iter() .map(|author| author.format_inline()) .collect::>() .join("\n"); @@ -71,8 +79,8 @@ fn format_translators(translators: Vec, count: usize) -> String { match !translators.is_empty() { true => { - let formated_translators = translators.clone()[..min(count, translators.len())] - .into_iter() + let formated_translators = translators[..min(count, translators.len())] + .iter() .map(|translator| translator.format_inline()) .collect::>() .join("\n"); @@ -91,9 +99,9 @@ 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)) + let formated_sequences: String = sequences[..min(count, sequences.len())] + .iter() + .map(|sequence| sequence.format(NO_LIMIT).result) .collect::>() .join("\n"); @@ -111,8 +119,8 @@ 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() + let formated_genres: String = genres[..min(count, genres.len())] + .iter() .map(|genre| genre.format()) .collect::>() .join("\n"); @@ -125,7 +133,7 @@ fn format_genres(genres: Vec, count: usize) -> String { } impl Format for Author { - fn format(&self, _max_size: u32) -> String { + fn format(&self, _max_size: usize) -> FormatResult { let Author { id, last_name, @@ -141,23 +149,37 @@ impl Format for Author { false => "".to_string(), }; - format!("{title}{link}{annotation}") + let result = format!("{title}{link}{annotation}"); + let result_len = result.len(); + + FormatResult { + result, + current_size: result_len, + max_size: result_len + } } } impl Format for Sequence { - fn format(&self, _max_size: u32) -> String { + fn format(&self, _max_size: usize) -> FormatResult { let Sequence { id, name, .. } = self; let title = format!("📚 {name}"); let link = format!("/s_{id}"); - format!("{title} {link}") + let result = format!("{title} {link}"); + let result_len = result.len(); + + FormatResult { + result, + current_size: result_len, + max_size: result_len + } } } impl Format for Translator { - fn format(&self, _max_size: u32) -> String { + fn format(&self, _max_size: usize) -> FormatResult { let Translator { id, last_name, @@ -173,7 +195,14 @@ impl Format for Translator { false => "".to_string(), }; - format!("{title}{link}{annotation}") + let result = format!("{title}{link}{annotation}"); + let result_len = result.len(); + + FormatResult { + result, + current_size: result_len, + max_size: result_len + } } } @@ -254,18 +283,30 @@ struct FormatVectorsResult { translators: String, sequences: String, genres: String, + + max_result_size: usize, } impl FormatVectorsResult { fn len(&self) -> usize { self.authors.len() + self.translators.len() + self.sequences.len() + self.genres.len() } + + fn with_max_result_size(self, max_result_size: usize) -> Self { + let Self { authors, translators, sequences, genres, .. } = self; + + Self { + authors, + translators, + sequences, + genres, + max_result_size + } + } } impl Book { - fn format_vectors(&self, max_size: u32) -> FormatVectorsResult { - let max_size_u: usize = max_size.try_into().unwrap(); - + fn format_vectors(&self, max_size: usize) -> FormatVectorsResult { let mut counts = FormatVectorsCounts { authors: self.authors.len(), translators: self.translators.len(), @@ -278,9 +319,12 @@ impl Book { translators: format_translators(self.translators.clone(), counts.translators), sequences: format_sequences(self.sequences.clone(), counts.sequences), genres: format_genres(self.genres.clone(), counts.genres), + max_result_size: 0 }; - while result.len() > max_size_u && counts.can_sub() { + let max_result_size = result.len(); + + while result.len() > max_size && counts.can_sub() { counts = counts.sub(); result = FormatVectorsResult { @@ -288,15 +332,16 @@ impl Book { translators: format_translators(self.translators.clone(), counts.translators), sequences: format_sequences(self.sequences.clone(), counts.sequences), genres: format_genres(self.genres.clone(), counts.genres), + max_result_size: 0 }; } - result + result.with_max_result_size(max_result_size) } } impl Format for Book { - fn format(&self, max_size: u32) -> String { + fn format(&self, max_size: usize) -> FormatResult { let book_title = { let Book { title, lang, .. } = self; @@ -319,29 +364,36 @@ impl Format for Book { let download_command = (StartDownloadData { id: self.id }).to_string(); let download_links = format!("Скачать:\n📥{download_command}"); - let required_data_len: u32 = format!("{book_title}{annotations}{download_links}").len().try_into().unwrap(); - let FormatVectorsResult { authors, translators, sequences, genres } = self.format_vectors( + let required_data_len: usize = format!("{book_title}{annotations}{download_links}").len(); + let FormatVectorsResult { authors, translators, sequences, genres, max_result_size } = self.format_vectors( max_size - required_data_len ); - format!("{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}") + let result = format!("{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}"); + let result_len = result.len(); + + FormatResult { + result, + current_size: result_len, + max_size: max_result_size + required_data_len + } } } impl Format for SearchBook { - fn format(&self, max_size: u32) -> String { + fn format(&self, max_size: usize) -> FormatResult { self.clone().as_book().format(max_size) } } impl Format for AuthorBook { - fn format(&self, max_size: u32) -> String { + fn format(&self, max_size: usize) -> FormatResult { self.clone().as_book().format(max_size) } } impl Format for TranslatorBook { - fn format(&self, max_size: u32) -> String { + fn format(&self, max_size: usize) -> FormatResult { 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 cf87a16..85d3208 100644 --- a/src/bots/approved_bot/services/book_library/types.rs +++ b/src/bots/approved_bot/services/book_library/types.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -use super::formaters::Format; +use super::formaters::{Format, FormatResult}; #[derive(Deserialize, Debug, Clone)] @@ -76,16 +76,63 @@ impl Page where T: Format + Clone, { - 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 - 3 * items_count) / items_count; + pub fn format_items(&self, max_size: usize) -> String { + let separator = "\n\n\n"; + let separator_len: usize = separator.len(); + + let items_count: usize = self.items.len(); + let item_size: usize = (max_size - separator_len * items_count) / items_count; + + let format_result: Vec = self.items + .clone() + .into_iter() + .map(|item| item.format(item_size)) + .collect(); + + let has_any_spliced = { + format_result + .clone() + .into_iter() + .any(|item| item.current_size != item.max_size) + }; + + if !has_any_spliced { + return format_result + .into_iter() + .map(|item| item.result) + .collect::>() + .join(separator); + } + + let mut free_symbols: usize = format_result + .clone() + .into_iter() + .filter(|item| item.current_size == item.max_size) + .map(|item| item_size - item.current_size) + .collect::>() + .into_iter() + .sum(); self.items .clone() .into_iter() - .map(|item| item.format(item_size)) + .enumerate() + .map(|(index, item)| { + let already_formated_result = &format_result[index]; + + if already_formated_result.current_size == already_formated_result.max_size { + already_formated_result.result.clone() + } else { + let new_item_size = item_size + free_symbols; + let new_formated_result = item.format(new_item_size); + + free_symbols = new_item_size - new_formated_result.current_size; + + new_formated_result.result + } + }) .collect::>() - .join("\n\n\n") + .join(separator) } } @@ -106,7 +153,7 @@ pub struct AuthorAnnotation { } pub trait AsBook { - fn as_book(self) -> T; + fn as_book(&self) -> T; } #[derive(Deserialize, Debug, Clone)] @@ -129,8 +176,8 @@ pub struct Book { } impl AsBook for Book { - fn as_book(self) -> Book { - self + fn as_book(&self) -> Self { + self.clone() } } @@ -149,16 +196,16 @@ pub struct SearchBook { } impl AsBook for SearchBook { - fn as_book(self) -> Book { + fn as_book(&self) -> Book { Book { id: self.id, - title: self.title, - lang: self.lang, - available_types: self.available_types, + title: self.title.clone(), + lang: self.lang.clone(), + available_types: self.available_types.clone(), annotation_exists: self.annotation_exists, - authors: self.authors, - translators: self.translators, - sequences: self.sequences, + authors: self.authors.clone(), + translators: self.translators.clone(), + sequences: self.sequences.clone(), genres: vec![], pages: None } @@ -179,16 +226,16 @@ pub struct AuthorBook { } impl AsBook for AuthorBook { - fn as_book(self) -> Book { + fn as_book(&self) -> Book { Book { id: self.id, - title: self.title, - lang: self.lang, - available_types: self.available_types, + title: self.title.clone(), + lang: self.lang.clone(), + available_types: self.available_types.clone(), annotation_exists: self.annotation_exists, authors: vec![], - translators: self.translators, - sequences: self.sequences, + translators: self.translators.clone(), + sequences: self.sequences.clone(), genres: vec![], pages: None } @@ -209,16 +256,16 @@ pub struct TranslatorBook { } impl AsBook for TranslatorBook { - fn as_book(self) -> Book { + fn as_book(&self) -> Book { Book { id: self.id, - title: self.title, - lang: self.lang, - available_types: self.available_types, + title: self.title.clone(), + lang: self.lang.clone(), + available_types: self.available_types.clone(), annotation_exists: self.annotation_exists, - authors: self.authors, + authors: self.authors.clone(), translators: vec![], - sequences: self.sequences, + sequences: self.sequences.clone(), genres: vec![], pages: None }