diff --git a/Cargo.lock b/Cargo.lock index 71fa1ff..9348267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "patched_porobot" -version = "0.7.1" +version = "0.8.0" dependencies = [ "data-encoding", "glob", diff --git a/Cargo.toml b/Cargo.toml index 67a6408..ec65759 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "patched_porobot" -version = "0.7.1" +version = "0.8.0" authors = ["Stefano Pigozzi "] edition = "2021" description = "Legends of Runeterra card database utilities and bots" @@ -23,9 +23,9 @@ regex = { version = "1.6.0" } lazy_static = { version = "1.4.0" } data-encoding = { version = "2.3.2" } varint-rs = { version = "2.2.0" } +glob = { version = "0.3.0" } # exec pretty_env_logger = { version = "0.4.0", optional = true } -glob = { version = "0.3.0", optional = true } # data serde = { version = "1.0.140", features = ["derive"] } serde_json = { version = "1.0.82" } @@ -43,7 +43,7 @@ rand = { version = "0.8.5", optional = true } [features] # data = [] # Always included -exec = ["pretty_env_logger", "glob"] +exec = ["pretty_env_logger"] search = ["tantivy"] telegram = ["exec", "search", "teloxide", "reqwest", "tokio", "md5", "rand"] discord = ["exec", "search"] diff --git a/src/data/corebundle/mod.rs b/src/data/corebundle/mod.rs index c05c4b5..4ebd771 100644 --- a/src/data/corebundle/mod.rs +++ b/src/data/corebundle/mod.rs @@ -46,7 +46,7 @@ impl CoreBundle { let locale = metadata.locale().ok_or(LoadingError::GettingLocale)?; let globals_path = &bundle_path - .join(&locale) + .join(locale) .join("data") .join(format!("globals-{}.json", &locale)); @@ -59,3 +59,19 @@ impl CoreBundle { }) } } + +/// Create [`globals::LocalizedGlobalsIndexes`] from the core bundle in the current working directory. +/// +/// This function tries to load data from the first directory matching the [glob] `./data/core-*`. +pub fn create_globalindexes_from_wd() -> globals::LocalizedGlobalsIndexes { + let path = glob::glob("./data/core-*") + .expect("glob to be a valid glob") + .filter_map(Some) + .find_map(Result::ok) + .expect("a valid core bundle to exist"); + + let core = CoreBundle::load(&path) + .expect("to be able to load `core-en_us` bundle"); + + globals::LocalizedGlobalsIndexes::from(core.globals) +} diff --git a/src/data/deckcode/deck.rs b/src/data/deckcode/deck.rs index 1a2ac12..4a32c89 100644 --- a/src/data/deckcode/deck.rs +++ b/src/data/deckcode/deck.rs @@ -2,13 +2,15 @@ use super::format::DeckCodeFormat; use crate::data::deckcode::version::{DeckCodeVersion, DeckCodeVersioned}; +use crate::data::setbundle::card::{Card, CardIndex}; use crate::data::setbundle::code::CardCode; use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::set::CardSet; use itertools::Itertools; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::io::{Cursor, Read, Write}; use varint_rs::{VarintReader, VarintWriter}; +use crate::data::setbundle::supertype::CardSupertype; /// A unshuffled Legends of Runeterra card deck. #[derive(Clone, Debug, PartialEq, Eq)] @@ -408,6 +410,170 @@ impl Deck { Ok(Self::encode_code(&cursor.into_inner())) } + + /// Get an [`Iterator`] of all Champion [`Card`]s in the deck. + pub fn champions<'a>(&'a self, cards: &'a CardIndex) -> impl Iterator { + self.contents.keys() + .filter_map(|cc| cc.to_card(cards)) + .filter(|c| c.supertype == CardSupertype::Champion) + } + + /// Count the number of copies of the given card present in this deck. + /// + /// # Example + /// + /// ```rust + /// use patched_porobot::deck; + /// use patched_porobot::data::deckcode::deck::Deck; + /// use patched_porobot::data::setbundle::code::CardCode; + /// + /// let d: Deck = deck![ + /// "01DE002": 40, + /// ]; + /// + /// let copies = d.copies_of(&CardCode::from("01DE002".to_string())); + /// assert_eq!(copies, 40); + /// ``` + pub fn copies_of(&self, code: &CardCode) -> u32 { + self.contents.get(code).map_or(0, ToOwned::to_owned) + } + + /// Get the number of cards in the deck. + /// + /// In the *Standard* and *Singleton* formats, this is never more than 40. + /// + /// # Example + /// + /// ```rust + /// use patched_porobot::deck; + /// use patched_porobot::data::deckcode::deck::Deck; + /// use patched_porobot::data::setbundle::card::CardIndex; + /// use patched_porobot::data::setbundle::create_cardindex_from_wd; + /// + /// let index: CardIndex = create_cardindex_from_wd(); + /// let deck: Deck = deck!("CECQCAQCA4AQIAYKAIAQGLRWAQAQECAPEUXAIAQDAEBQOCIBAIAQEMJYAA"); + /// assert_eq!(d.card_count(&index), 40); + /// + pub fn card_count(&self) -> u32 { + self.contents.values().sum() + } + + /// Get the number of champion cards in the deck. + /// + /// In the *Standard* and *Singleton* format, this is never more than 6. + /// + /// # Example + /// + /// ```rust + /// use patched_porobot::deck; + /// use patched_porobot::data::deckcode::deck::Deck; + /// use patched_porobot::data::setbundle::card::CardIndex; + /// use patched_porobot::data::setbundle::create_cardindex_from_wd; + /// + /// let index: CardIndex = create_cardindex_from_wd(); + /// let deck: Deck = deck!("CECQCAQCA4AQIAYKAIAQGLRWAQAQECAPEUXAIAQDAEBQOCIBAIAQEMJYAA"); + /// assert_eq!(d.champions_count(&index), 6); + /// ``` + pub fn champions_count(&self, cards: &CardIndex) -> u32 { + self.champions(cards) + .map(|c| &c.code) + .map(|cc| self.copies_of(cc)) + .sum() + } + + /// Find the first possible set of regions that the [`Deck`] fits in. + /// + /// Lower amounts of regions are preferred: the limit will be increased from 1 up to `limit` until a valid solution is found. + /// + /// # Warning + /// + /// This method traverses the tree of possible region selections until one is found. + /// + /// The time required to run this function may grow exponentially with the amount of cards in the deck! + pub fn regions(&self, card_index: &CardIndex, limit: usize) -> Option> { + let cards: Vec<&Card> = self.contents.keys() + .flat_map(|cc| cc.to_card(card_index)) + .collect(); + + for n in 1..=limit { + let result = Self::regions_recursive_first_limit(cards.as_slice(), HashSet::new(), n); + + if result.is_some() { + return result; + } + } + + None + } + + /// Find the first possible set of regions that covers all given [`Card`]s. + /// + /// This function is *recursive*: the `cards` parameter holds the [`Card`]s left to process, while the `regions` parameter holds the regions found so far, and the `limit` parameter holds the size of the `regions` set to stop the recursion at. + /// + /// # Warning + /// + /// This method traverses the tree of possible region selections until one is found. + /// + /// The time required to run this function may grow exponentially with the size of `cards`! + fn regions_recursive_first_limit(cards: &[&Card], regions: HashSet, limit: usize) -> Option> { + match cards.get(0) { + None => Some(regions), + Some(card) => { + card.regions.iter() + .map(|region| { + match region { + CardRegion::Unsupported => { + None + } + CardRegion::Runeterra => { + Self::regions_recursive_first_limit(&cards[1..], regions.clone(), limit) + } + _ => { + let mut regions = regions.clone(); + let inserted = regions.insert(*region); + match inserted && regions.len() > limit { + true => None, + false => Self::regions_recursive_first_limit(&cards[1..], regions, limit) + } + } + } + }) + .find(Option::is_some) + .unwrap_or(None) + }, + } + } + + /// Check if the [`Deck`] is legal for play in the *Standard* format. + /// + /// # Returns + /// + /// - `None` if the deck is not legal for *Standard* play. + /// - `Some(regions)` if the deck is legal for *Standard* play considering the specified region set. + pub fn standard(&self, cards: &CardIndex) -> Option> { + let copies_limit = self.contents.values().all(|n| n <= &3); + let cards_limit = self.card_count() == 40; + let champions_limit = self.champions_count(cards) <= 6; + let regions = self.regions(cards, 2); + + match copies_limit && cards_limit && champions_limit { + false => None, + true => regions, + } + } + + /// Check if the [`Deck`] is legal for play in the *Singleton* format. + pub fn singleton(&self, cards: &CardIndex) -> Option> { + let copies_limit = self.contents.values().all(|n| n <= &1); + let cards_limit = self.card_count() == 40; + let champions_limit = self.champions_count(cards) <= 6; + let regions = self.regions(cards, 3); + + match copies_limit && cards_limit && champions_limit { + false => None, + true => regions, + } + } } /// An error occoured while decoding a [`Deck`] from a code. @@ -448,9 +614,26 @@ pub type DeckDecodingResult = Result; /// The [`Result`] of a [`Deck`] **encoding** operation, for example [`Deck::to_code`]. pub type DeckEncodingResult = Result; -/// Macro to build a deck from card code strings and quantities. +/// Macro to build a deck. /// -/// # Example +/// Useful to quickly build [`Deck`]s from trusted input, such as when creating tests. +/// +/// It can build a deck: +/// +/// - from a deck code; +/// - from card code strings and quantities. +/// +/// # Panics +/// +/// If the deck code is not valid. +/// +/// # Examples +/// +/// ```rust +/// use patched_porobot::deck; +/// +/// let _my_deck = deck!("CECQCAQCA4AQIAYKAIAQGLRWAQAQECAPEUXAIAQDAEBQOCIBAIAQEMJYAA"); +/// ``` /// /// ```rust /// use patched_porobot::deck; @@ -480,13 +663,17 @@ pub type DeckEncodingResult = Result; /// ``` #[macro_export] macro_rules! deck { - [$($cd:literal: $qty:literal),* $(,)?] => { + [ $($cd:literal: $qty:expr),* $(,)? ] => { $crate::data::deckcode::deck::Deck { contents: std::collections::HashMap::from([ $(($crate::data::setbundle::code::CardCode { full: $cd.to_string() }, $qty),)* ]) } - } + }; + + ( $code:expr ) => { + $crate::data::deckcode::deck::Deck::from_code($code).expect("deck code created with deck!() to be valid") + }; } #[rustfmt::skip::macros(test_de_ser, test_ser_de)] @@ -653,5 +840,64 @@ mod tests { "02BW012": 69, ]); - //test_ser_de!(test_ser_de_, deck![]); + // test_ser_de!(test_ser_de_, deck![]); + + macro_rules! test_legality { + ( $id:ident, $deck:expr, $check:path, $assert:expr ) => { + #[test] + fn $id() { + let index = $crate::data::setbundle::create_cardindex_from_wd(); + let deck: Deck = $deck; + let result = $check(&deck, &index).is_some(); + assert_eq!(result, $assert); + } + } + } + + test_legality!( + test_legality_standard_lonelyporo1, + deck!("CEAAAAIBAEAQQ"), + Deck::standard, false + ); + test_legality!( + test_legality_standard_twistedshrimp, + deck!("CICACBAFAEBAGBQICABQCBJLF4YQOAQGAQEQYEQUDITAAAIBAMCQO"), + Deck::standard, true + ); + test_legality!( + test_legality_standard_poros, + deck!("CQDQCAQBAMAQGAICAECACDYCAECBIFYCAMCBEEYCAUFIYANAAEBQCAIICA2QCAQBAEVTSAA"), + Deck::standard, true + ); + test_legality!( + test_legality_standard_sand, + deck!("CMBAGBAHANTXEBQBAUCAOFJGFIYQEAIBAUOQIBAHGM5HM6ICAECAOOYCAECRSGY"), + Deck::standard, true + ); + + test_legality!( + test_legality_singleton_lonelyporo1, + deck!("CEAAAAIBAEAQQ"), + Deck::singleton, false + ); + test_legality!( + test_legality_singleton_twistedshrimp, + deck!("CICACBAFAEBAGBQICABQCBJLF4YQOAQGAQEQYEQUDITAAAIBAMCQO"), + Deck::singleton, false + ); + test_legality!( + test_legality_singleton_poros, + deck!("CQDQCAQBAMAQGAICAECACDYCAECBIFYCAMCBEEYCAUFIYANAAEBQCAIICA2QCAQBAEVTSAA"), + Deck::singleton, false + ); + test_legality!( + test_legality_singleton_sand, + deck!("CMBAGBAHANTXEBQBAUCAOFJGFIYQEAIBAUOQIBAHGM5HM6ICAECAOOYCAECRSGY"), + Deck::singleton, false + ); + test_legality!( + test_legality_singleton_paltri, + deck!("CQAAADABAICACAIFBLAACAIFAEHQCBQBEQBAGBADAQBAIAIKBUBAKBAWDUBQIBACA4GAMAIBAMCAYHJBGADAMBAOCQKRMKBLA4AQIAQ3D4QSIKZYBACAODJ3JRIW3AABQIAYUAI"), + Deck::singleton, true + ); } diff --git a/src/data/deckcode/version.rs b/src/data/deckcode/version.rs index 216721a..985b5bf 100644 --- a/src/data/deckcode/version.rs +++ b/src/data/deckcode/version.rs @@ -77,9 +77,6 @@ impl DeckCodeVersioned for CardRegion { CardRegion::BandleCity => Some(DeckCodeVersion::V4), CardRegion::Runeterra => Some(DeckCodeVersion::V5), - CardRegion::Jhin => Some(DeckCodeVersion::V5), - CardRegion::Evelynn => Some(DeckCodeVersion::V5), - CardRegion::Bard => Some(DeckCodeVersion::V5), _ => None, } diff --git a/src/data/setbundle/code.rs b/src/data/setbundle/code.rs index 26a11bb..5fc78b4 100644 --- a/src/data/setbundle/code.rs +++ b/src/data/setbundle/code.rs @@ -2,7 +2,7 @@ use crate::data::setbundle::card::{Card, CardIndex}; -/// The internal code of a [Card](super::card::Card). +/// The internal code of a [`Card`]. /// /// It is a ASCII string composed of the following segments: /// - `0..2`: set; @@ -98,3 +98,10 @@ impl From for CardCode { CardCode { full } } } + +/// Extract the card code from a [`Card`]. +impl From for CardCode { + fn from(c: Card) -> Self { + c.code + } +} \ No newline at end of file diff --git a/src/data/setbundle/mod.rs b/src/data/setbundle/mod.rs index 2785076..8ebb50d 100644 --- a/src/data/setbundle/mod.rs +++ b/src/data/setbundle/mod.rs @@ -6,7 +6,7 @@ use super::anybundle::metadata::BundleMetadata; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; use std::fs::File; -use std::path::Path; +use std::path::{Path, PathBuf}; pub mod art; pub mod card; @@ -50,7 +50,7 @@ impl SetBundle { let mut json_filename = name.to_os_string(); json_filename.push(".json"); - &bundle_path.join(&locale).join("data").join(&json_filename) + &bundle_path.join(locale).join("data").join(&json_filename) }; let name = name @@ -70,3 +70,37 @@ impl SetBundle { }) } } + + +/// Create a [`card::CardIndex`] from set bundles in the given paths. +/// +/// # Panics +/// +/// If any of the required files cannot be loaded (see [`SetBundle::load`]). +pub fn create_cardindex_from_paths(paths: impl Iterator) -> card::CardIndex { + let mut index = card::CardIndex::new(); + for path in paths { + let set = SetBundle::load(&path).expect("to be able to load SetBundle"); + for card in set.cards { + index.insert(card.code.clone(), card); + } + }; + index +} + +/// Create a [`card::CardIndex`] from set bundles in the current working directory. +/// +/// This function tries to load data from any directory matching the [glob] `./data/set*-*`. +/// +/// # Panics +/// +/// See [`create_cardindex_from_paths`]. +pub fn create_cardindex_from_wd() -> card::CardIndex { + let paths = glob::glob("./data/set*-*") + .expect("glob to be a valid glob") + .filter_map(Some) + .filter_map(Result::ok); + + create_cardindex_from_paths(paths) +} + diff --git a/src/data/setbundle/region.rs b/src/data/setbundle/region.rs index f2fb1c4..3df046b 100644 --- a/src/data/setbundle/region.rs +++ b/src/data/setbundle/region.rs @@ -32,22 +32,15 @@ pub enum CardRegion { /// Runeterra. Runeterra, - /// Origin: The Virtuoso. - Jhin, - /// Origin: Agony's Embrace. - Evelynn, - /// Origin: The Wandering Caretaker. - Bard, - /// Unsupported region. #[serde(other)] Unsupported, } impl CardRegion { - /// Get the [LocalizedCardRegion] associated with this [CardRegion]. + /// Get the [`LocalizedCardRegion`] associated with this [`CardRegion`]. /// - /// Returns [Option::None] if no matching [LocalizedCardRegion] was found, for example for [CardRegion::Unsupported] regions. + /// Returns [`None`] if no matching [`LocalizedCardRegion`] was found, for example for [`CardRegion::Unsupported`] regions. /// /// Equivalent to calling [LocalizedCardRegionIndex::get]. pub fn localized<'hm>( @@ -79,7 +72,7 @@ impl CardRegion { /// Get the short code of this [`CardRegion`]. /// - /// If the region has no short code, it will return [`Option::None`]. + /// If the region has no short code, it will return [`None`]. pub fn to_code(&self) -> Option { match self { Self::Demacia => Some("DE".to_string()), @@ -122,7 +115,7 @@ impl From for CardRegion { /// Get the internal id of this [`CardRegion`]. /// -/// If the region has no internal id, it will return [`Result::Err`]. +/// If the region has no internal id, it will return [`Err`]. impl TryFrom for u32 { type Error = (); @@ -169,8 +162,5 @@ mod tests { test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardRegion::PiltoverZaun); test_deserialization!(deserialize_bandlecity, r#""BandleCity""#, CardRegion::BandleCity); test_deserialization!(deserialize_runeterra, r#""Runeterra""#, CardRegion::Runeterra); - test_deserialization!(deserialize_jhin, r#""Jhin""#, CardRegion::Jhin); - test_deserialization!(deserialize_evelynn, r#""Evelynn""#, CardRegion::Evelynn); - test_deserialization!(deserialize_bard, r#""Bard""#, CardRegion::Bard); test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardRegion::Unsupported); } diff --git a/src/data/setbundle/speed.rs b/src/data/setbundle/speed.rs index 8b665c8..cfa32c5 100644 --- a/src/data/setbundle/speed.rs +++ b/src/data/setbundle/speed.rs @@ -2,7 +2,7 @@ use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex}; -/// A possible [Spell](super::type::CardType::Spell) speed. +/// A possible [`Spell`](super::r#type::CardType::Spell) speed. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum SpellSpeed { /// Non-spell cards have this speed. @@ -17,11 +17,11 @@ pub enum SpellSpeed { } impl SpellSpeed { - /// Get the [LocalizedSpellSpeed] associated with this [SpellSpeed]. + /// Get the [`LocalizedSpellSpeed`] associated with this [`SpellSpeed`]. /// - /// Returns [Option::None] if no matching [LocalizedSpellSpeed] was found, for example spell speeds missing from the index. + /// Returns [`None`] if no matching [`LocalizedSpellSpeed`] was found, for example spell speeds missing from the index. /// - /// Equivalent to calling [LocalizedSpellSpeedIndex::get]. + /// Equivalent to calling [`LocalizedSpellSpeedIndex::get`]. pub fn localized<'hm>( &self, hm: &'hm LocalizedSpellSpeedIndex, diff --git a/src/telegram/display.rs b/src/telegram/display.rs index 5e40692..b6cdab9 100644 --- a/src/telegram/display.rs +++ b/src/telegram/display.rs @@ -2,6 +2,8 @@ //! //! [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style +use std::cmp::Ordering::{Equal, Greater, Less}; +use std::collections::HashSet; use crate::data::corebundle::globals::LocalizedGlobalsIndexes; use crate::data::corebundle::keyword::LocalizedCardKeywordIndex; use crate::data::corebundle::region::LocalizedCardRegionIndex; @@ -161,33 +163,79 @@ fn display_levelup(levelup: &String) -> String { /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&str>) -> String { - // TODO: optimize this let cards = deck .contents .keys() - .sorted_by(|a, b| { - let card_a = index.get(a).expect("card to exist in the index"); - let card_b = index.get(b).expect("card to exist in the index"); + .map(|k| ( + index.get(k), + deck.contents.get(k).expect("CardCode from Deck to have a quantity"), + )) + .sorted_by(|(opt_a, _), (opt_b, _)| { + if opt_a.is_none() && opt_b.is_none() { + return Equal; + } + if opt_b.is_none() { + return Greater; + } + else if opt_a.is_none() { + return Less; + } + + let card_a = opt_a.expect("opt_a to be Some"); + let card_b = opt_b.expect("opt_b to be Some"); card_a .cost .cmp(&card_b.cost) .then(card_a.name.cmp(&card_b.name)) }) - .map(|k| { - let card = index.get(k).expect("card to exist in the index"); - let quantity = deck.contents.get(k).unwrap(); + .map(|(card, quantity)| { + let name = match card { + None => "Unknown Card".to_string(), + Some(card) => match card.supertype { + CardSupertype::Champion => format!("{}", escape(&card.name)), + _ => escape(&card.name), + } + }; - if card.supertype == CardSupertype::Champion { - format!("{}× {}", &quantity, &card.name) - } else { - format!("{}× {}", &quantity, &card.name) - } + format!("{}× {}", &quantity, &name) }) .join("\n"); + let mut tags: Vec<&'static str> = vec![]; + + let regions = if let Some(regions) = deck.standard(&index) { + tags.push("#Standard"); + regions + } else if let Some(regions) = deck.singleton(&index) { + tags.push("#Singleton"); + regions + } else { + HashSet::new() + }; + + for region in regions { + tags.push(match region { + CardRegion::Noxus => "#Noxus", + CardRegion::Demacia => "#Demacia", + CardRegion::Freljord => "#Freljord", + CardRegion::ShadowIsles => "#ShadowIsles", + CardRegion::Targon => "#Targon", + CardRegion::Ionia => "#Ionia", + CardRegion::Bilgewater => "#Bilgewater", + CardRegion::Shurima => "#Shurima", + CardRegion::PiltoverZaun => "#PiltoverZaun", + CardRegion::BandleCity => "#BandleCity", + CardRegion::Runeterra => "#Runeterra", + CardRegion::Unsupported => "Unknown", + }) + } + + let tags = tags.join(", "); + let tags = if tags.len() > 0 { format!("{}\n", &tags) } else { "".to_string() }; + match name { - Some(name) => format!("{}\n{}\n\n{}", &name, &code, &cards), - None => format!("{}\n\n{}", &code, &cards), + Some(name) => format!("{}\n{}\n{}\n{}", &name, &code, &tags, &cards), + None => format!("{}\n{}\n{}", &code, &tags, &cards), } } diff --git a/src/telegram/inline.rs b/src/telegram/inline.rs index 8a5da10..1d11ddf 100644 --- a/src/telegram/inline.rs +++ b/src/telegram/inline.rs @@ -55,6 +55,8 @@ pub fn deck_to_inlinequeryresult( .to_code(DeckCodeFormat::F1) .expect("serialized deck to deserialize properly"); + let message_text = display_deck(index, deck, &code, &name); + InlineQueryResult::Article(InlineQueryResultArticle { id: format!("{}:{:x}", &crystal, md5::compute(&code)), title: match &name { diff --git a/src/telegram/main.rs b/src/telegram/main.rs index aacb08d..8139f69 100644 --- a/src/telegram/main.rs +++ b/src/telegram/main.rs @@ -1,14 +1,10 @@ //! This module defines the [`main`] function for `patched_porobot_telegram`. -use crate::data::corebundle::globals::LocalizedGlobalsIndexes; -use crate::data::corebundle::CoreBundle; -use crate::data::setbundle::card::{Card, CardIndex}; -use crate::data::setbundle::SetBundle; +use crate::data::corebundle::create_globalindexes_from_wd; +use crate::data::setbundle::create_cardindex_from_wd; use crate::search::cardsearch::CardSearchEngine; use crate::telegram::handler::{inline_query_handler, message_handler}; -use glob::glob; use log::*; -use std::path::PathBuf; use rand::Rng; use teloxide::prelude::*; @@ -17,43 +13,17 @@ pub async fn main() { pretty_env_logger::init(); debug!("Logger initialized successfully!"); - debug!("Loading core bundle..."); - let core = CoreBundle::load(&*PathBuf::from("./data/core-en_us")) - .expect("to be able to load `core-en_us` bundle"); - debug!("Loaded core bundle successfully!"); + debug!("Creating LocalizedGlobalIndexes..."); + let globals = create_globalindexes_from_wd(); + debug!("Created LocalizedGlobalIndexes!"); - debug!("Loading set bundles..."); - let setpaths = glob("./data/set*-*") - .expect("setglob to be a valid glob") - .into_iter() - .filter(|sp| sp.is_ok()) - .map(|sp| sp.unwrap()); - let mut cards: Vec = vec![]; - for setpath in setpaths { - debug!("Loading {:?}...", &setpath); - let set = SetBundle::load(&setpath).expect(&*format!( - "to be able to load {:?} as a set bundle", - &setpath - )); - let mut setcards = set.cards; - cards.append(&mut setcards); - } - debug!("Loaded {} cards!", &cards.len()); + debug!("Creating CardIndex..."); + let cards = create_cardindex_from_wd(); + debug!("Created CardIndex!"); - debug!("Indexing globals..."); - let globals = LocalizedGlobalsIndexes::from(core.globals); - debug!("Indexed globals!"); - - let mut index = CardIndex::new(); - for card in cards { - index.insert(card.code.clone(), card); - } - let cards = index; - debug!("Indexed cards!"); - - debug!("Creating search engine..."); + debug!("Creating CardSearchEngine..."); let engine = CardSearchEngine::new(globals, cards); - debug!("Created search engine!"); + debug!("Created CardSearchEngine!"); debug!("Creating Telegram bot with parameters from the environment..."); let bot = Bot::from_env();