diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..e69de29 diff --git a/src/bin/patched_porobot_telegram.rs b/src/bin/patched_porobot_telegram.rs index 40c1352..41e7e30 100644 --- a/src/bin/patched_porobot_telegram.rs +++ b/src/bin/patched_porobot_telegram.rs @@ -77,7 +77,6 @@ #![doc(html_logo_url = "https://raw.githubusercontent.com/Steffo99/patched-porobot/main/icon.png")] - #[doc(hidden)] #[tokio::main] async fn main() { diff --git a/src/data/anybundle/metadata.rs b/src/data/anybundle/metadata.rs index 8d6dbb7..5c4b51c 100644 --- a/src/data/anybundle/metadata.rs +++ b/src/data/anybundle/metadata.rs @@ -2,9 +2,9 @@ //! //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon +use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; use std::fs::File; use std::path::Path; -use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; /// A parsed `metadata.json` file from a Data Dragon Bundle. /// @@ -24,17 +24,15 @@ pub struct BundleMetadata { /// [Vec] of locales included in the bundle. /// /// The specification defines that there can be multiple, but currently I've never seen more (or less) than one. - pub locales: Vec + pub locales: Vec, } - impl BundleMetadata { /// Load a `metadata.json` file to create a [BundleMetadata] instance. pub fn load(path: &Path) -> LoadingResult { - let file = File::open(path) - .map_err(LoadingError::OpeningFile)?; - let data = serde_json::de::from_reader::(file) - .map_err(LoadingError::Deserializing)?; + let file = File::open(path).map_err(LoadingError::OpeningFile)?; + let data = + serde_json::de::from_reader::(file).map_err(LoadingError::Deserializing)?; Ok(data) } @@ -46,7 +44,6 @@ impl BundleMetadata { } } - #[cfg(test)] mod tests { use super::*; @@ -54,13 +51,16 @@ mod tests { #[test] fn deserialize() { assert_eq!( - serde_json::de::from_str::<'static, BundleMetadata>(r#" + serde_json::de::from_str::<'static, BundleMetadata>( + r#" { "locales": [ "en_us" ] } - "#).unwrap(), + "# + ) + .unwrap(), BundleMetadata { locales: vec!["en_us".to_string()] } diff --git a/src/data/anybundle/outcomes.rs b/src/data/anybundle/outcomes.rs index ca65037..f852fbd 100644 --- a/src/data/anybundle/outcomes.rs +++ b/src/data/anybundle/outcomes.rs @@ -2,7 +2,6 @@ //! //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon - /// An error that occoured while loading a Data Dragon Bundle. #[derive(Debug)] pub enum LoadingError { diff --git a/src/data/corebundle/globals.rs b/src/data/corebundle/globals.rs index 42b35a1..18c7ff5 100644 --- a/src/data/corebundle/globals.rs +++ b/src/data/corebundle/globals.rs @@ -1,14 +1,14 @@ //! Module defining structs representing data contained in `globals.json` files. +use super::keyword::{LocalizedCardKeywordIndex, LocalizedCardKeywordVec}; +use super::rarity::{LocalizedCardRarityIndex, LocalizedCardRarityVec}; +use super::region::{LocalizedCardRegionIndex, LocalizedCardRegionVec}; +use super::set::{LocalizedCardSetIndex, LocalizedCardSetVec}; +use super::speed::{LocalizedSpellSpeedIndex, LocalizedSpellSpeedVec}; +use super::vocabterm::{LocalizedVocabTermIndex, LocalizedVocabTermVec}; +use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; use std::fs::File; use std::path::Path; -use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; -use super::vocabterm::{LocalizedVocabTermVec, LocalizedVocabTermIndex}; -use super::keyword::{LocalizedCardKeywordVec, LocalizedCardKeywordIndex}; -use super::region::{LocalizedCardRegionVec, LocalizedCardRegionIndex}; -use super::speed::{LocalizedSpellSpeedVec, LocalizedSpellSpeedIndex}; -use super::rarity::{LocalizedCardRarityVec, LocalizedCardRarityIndex}; -use super::set::{LocalizedCardSetVec, LocalizedCardSetIndex}; /// A parsed `globals.json` file from a [Data Dragon] [Core Bundle]. /// @@ -68,19 +68,16 @@ pub struct LocalizedGlobalsIndexes { pub sets: LocalizedCardSetIndex, } - impl LocalizedGlobalsVecs { /// Load a `globals.json` file to create a [LocalizedGlobalsVecs] instance. pub fn load(path: &Path) -> LoadingResult { - let file = File::open(path) - .map_err(LoadingError::OpeningFile)?; - let data = serde_json::de::from_reader::(file) - .map_err(LoadingError::Deserializing)?; + let file = File::open(path).map_err(LoadingError::OpeningFile)?; + let data = + serde_json::de::from_reader::(file).map_err(LoadingError::Deserializing)?; Ok(data) } } - impl From for LocalizedGlobalsIndexes { fn from(o: LocalizedGlobalsVecs) -> Self { Self { @@ -130,7 +127,6 @@ impl From for LocalizedGlobalsIndexes { } } - #[cfg(test)] mod tests { use super::LocalizedGlobalsVecs; @@ -149,7 +145,8 @@ mod tests { #[test] fn deserialize() { assert_eq!( - serde_json::de::from_str::<'static, LocalizedGlobalsVecs>(r#" + serde_json::de::from_str::<'static, LocalizedGlobalsVecs>( + r#" { "vocabTerms": [ { @@ -193,50 +190,10 @@ mod tests { } ] } - "#).unwrap(), - LocalizedGlobalsVecs { - vocab_terms: vec![ - LocalizedVocabTerm { - vocabterm: "Allegiance".to_string(), - name: "Allegiance".to_string(), - description: "When you summon this, it gets its allegiance bonus if the top card of your deck matches its region.".to_string(), - } - ], - keywords: vec![ - LocalizedCardKeyword { - keyword: CardKeyword::SpellOverwhelm, - name: "Overwhelm".to_string(), - description: "Inflicts damage beyond what would kill the target(s) to the enemy Nexus.".to_string(), - } - ], - regions: vec![ - LocalizedCardRegion { - region: CardRegion::Noxus, - name: "Noxus".to_string(), - abbreviation: "NX".to_string(), - icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png".to_string(), - } - ], - spell_speeds: vec![ - LocalizedSpellSpeed { - spell_speed: SpellSpeed::Slow, - name: "Slow".to_string(), - } - ], - rarities: vec![ - LocalizedCardRarity { - rarity: CardRarity::Common, - name: "COMMON".to_string(), - } - ], - sets: vec![ - LocalizedCardSet { - set: CardSet::CallOfTheMountain, - name: "Call of the Mountain".to_string(), - icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/sets/set3_crispmip.png".to_string(), - } - ] - } + "# + ) + .unwrap(), + LocalizedGlobalsVecs { vocab_terms: vec![LocalizedVocabTerm { vocabterm: "Allegiance".to_string(), name: "Allegiance".to_string(), description: "When you summon this, it gets its allegiance bonus if the top card of your deck matches its region.".to_string() }], keywords: vec![LocalizedCardKeyword { keyword: CardKeyword::SpellOverwhelm, name: "Overwhelm".to_string(), description: "Inflicts damage beyond what would kill the target(s) to the enemy Nexus.".to_string() }], regions: vec![LocalizedCardRegion { region: CardRegion::Noxus, name: "Noxus".to_string(), abbreviation: "NX".to_string(), icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png".to_string() }], spell_speeds: vec![LocalizedSpellSpeed { spell_speed: SpellSpeed::Slow, name: "Slow".to_string() }], rarities: vec![LocalizedCardRarity { rarity: CardRarity::Common, name: "COMMON".to_string() }], sets: vec![LocalizedCardSet { set: CardSet::CallOfTheMountain, name: "Call of the Mountain".to_string(), icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/sets/set3_crispmip.png".to_string() }] } ) } } diff --git a/src/data/corebundle/keyword.rs b/src/data/corebundle/keyword.rs index 328065c..157f1e6 100644 --- a/src/data/corebundle/keyword.rs +++ b/src/data/corebundle/keyword.rs @@ -28,6 +28,7 @@ mod tests { use super::*; #[test] + #[rustfmt::skip] fn deserialize() { assert_eq!( serde_json::de::from_str::<'static, LocalizedCardKeyword>(r#" diff --git a/src/data/corebundle/mod.rs b/src/data/corebundle/mod.rs index f7f0037..c05c4b5 100644 --- a/src/data/corebundle/mod.rs +++ b/src/data/corebundle/mod.rs @@ -3,18 +3,17 @@ //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles -use std::path::Path; use super::anybundle::metadata::BundleMetadata; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; +use std::path::Path; pub mod globals; -pub mod vocabterm; pub mod keyword; -pub mod region; -pub mod speed; pub mod rarity; +pub mod region; pub mod set; - +pub mod speed; +pub mod vocabterm; /// A parsed [Data Dragon] [Core Bundle]. /// @@ -32,23 +31,19 @@ pub struct CoreBundle { pub globals: globals::LocalizedGlobalsVecs, } - impl CoreBundle { /// Load a Core Bundle directory to create a [CoreBundle] instance. pub fn load(bundle_path: &Path) -> LoadingResult { - let metadata = BundleMetadata::load( - &bundle_path - .join("metadata.json") - )?; + let metadata = BundleMetadata::load(&bundle_path.join("metadata.json"))?; - let name = bundle_path.file_name() + let name = bundle_path + .file_name() .ok_or(LoadingError::GettingBundleName)? .to_str() .ok_or(LoadingError::ConvertingBundleName)? .to_string(); - let locale = metadata.locale() - .ok_or(LoadingError::GettingLocale)?; + let locale = metadata.locale().ok_or(LoadingError::GettingLocale)?; let globals_path = &bundle_path .join(&locale) @@ -57,6 +52,10 @@ impl CoreBundle { let globals = globals::LocalizedGlobalsVecs::load(globals_path)?; - Ok(CoreBundle {name, metadata, globals}) + Ok(CoreBundle { + name, + metadata, + globals, + }) } } diff --git a/src/data/corebundle/rarity.rs b/src/data/corebundle/rarity.rs index d135906..92996f2 100644 --- a/src/data/corebundle/rarity.rs +++ b/src/data/corebundle/rarity.rs @@ -25,6 +25,7 @@ mod tests { use super::*; #[test] + #[rustfmt::skip] fn deserialize() { assert_eq!( serde_json::de::from_str::<'static, LocalizedCardRarity>(r#" diff --git a/src/data/corebundle/region.rs b/src/data/corebundle/region.rs index 34f94fc..ab40d39 100644 --- a/src/data/corebundle/region.rs +++ b/src/data/corebundle/region.rs @@ -1,7 +1,7 @@ //! Module defining structs representing localized card regions. -use std::collections::HashMap; use crate::data::setbundle::region::CardRegion; +use std::collections::HashMap; /// A Legends of Runeterra [CardRegion], and its associated localization. #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] @@ -28,7 +28,6 @@ pub type LocalizedCardRegionVec = Vec; /// An index of [LocalizedCardRegion]s, with [LocalizedCardRegion::region]s as keys. pub type LocalizedCardRegionIndex = HashMap; - #[cfg(test)] mod tests { use super::*; @@ -36,19 +35,22 @@ mod tests { #[test] fn deserialize() { assert_eq!( - serde_json::de::from_str::<'static, LocalizedCardRegion>(r#" + serde_json::de::from_str::<'static, LocalizedCardRegion>( + r#" { "abbreviation": "NX", "iconAbsolutePath": "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png", "name": "Noxus", "nameRef": "Noxus" } - "#).unwrap(), + "# + ) + .unwrap(), LocalizedCardRegion { region: CardRegion::Noxus, name: "Noxus".to_string(), abbreviation: "NX".to_string(), - icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png".to_string(), + icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png".to_string() } ); } diff --git a/src/data/corebundle/set.rs b/src/data/corebundle/set.rs index 3b68561..08e9334 100644 --- a/src/data/corebundle/set.rs +++ b/src/data/corebundle/set.rs @@ -29,6 +29,7 @@ mod tests { use super::*; #[test] + #[rustfmt::skip] fn deserialize() { assert_eq!( serde_json::de::from_str::<'static, LocalizedCardSet>(r#" diff --git a/src/data/corebundle/speed.rs b/src/data/corebundle/speed.rs index 71b232b..f7b8929 100644 --- a/src/data/corebundle/speed.rs +++ b/src/data/corebundle/speed.rs @@ -1,7 +1,7 @@ //! Module defining structs representing localized spell speeds. -use std::collections::HashMap; use crate::data::setbundle::speed::SpellSpeed; +use std::collections::HashMap; /// A Legends of Runeterra [SpellSpeed], and its associated localization. #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] @@ -19,7 +19,6 @@ pub type LocalizedSpellSpeedVec = Vec; /// An index of [LocalizedSpellSpeed]s, with [LocalizedSpellSpeed::spell_speed]s as keys. pub type LocalizedSpellSpeedIndex = HashMap; - #[cfg(test)] mod tests { use super::*; @@ -27,15 +26,18 @@ mod tests { #[test] fn deserialize() { assert_eq!( - serde_json::de::from_str::<'static, LocalizedSpellSpeed>(r#" + serde_json::de::from_str::<'static, LocalizedSpellSpeed>( + r#" { "name": "Slow", "nameRef": "Slow" } - "#).unwrap(), + "# + ) + .unwrap(), LocalizedSpellSpeed { spell_speed: SpellSpeed::Slow, - name: "Slow".to_string(), + name: "Slow".to_string() } ); } diff --git a/src/data/corebundle/vocabterm.rs b/src/data/corebundle/vocabterm.rs index 9fc6379..e8f4066 100644 --- a/src/data/corebundle/vocabterm.rs +++ b/src/data/corebundle/vocabterm.rs @@ -30,6 +30,7 @@ mod tests { use super::*; #[test] + #[rustfmt::skip] fn deserialize() { assert_eq!( serde_json::de::from_str::<'static, LocalizedVocabTerm>(r#" diff --git a/src/data/mod.rs b/src/data/mod.rs index eceb5b9..55f1ef7 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -2,6 +2,6 @@ //! //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon +pub mod anybundle; pub mod corebundle; pub mod setbundle; -pub mod anybundle; diff --git a/src/data/setbundle/art.rs b/src/data/setbundle/art.rs index 2aa8d22..2d3715b 100644 --- a/src/data/setbundle/art.rs +++ b/src/data/setbundle/art.rs @@ -1,6 +1,5 @@ //! Module defining [CardArt]. - use lazy_static::lazy_static; use regex::Regex; @@ -30,7 +29,6 @@ pub struct CardArt { pub full_png: String, } - impl CardArt { /// URL to the `.jpg` image of the `en_us` locale of the rendered card, via `poro.steffo.eu`. /// @@ -46,10 +44,16 @@ impl CardArt { lazy_static! { static ref GET_JPG: Regex = Regex::new( r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P[^/]+)/(?P[^/]+)/img/cards/(?P.+)[.]png$"# - ).unwrap(); + ) + .unwrap(); } - GET_JPG.replace_all(&self.card_png, "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg").to_string() + GET_JPG + .replace_all( + &self.card_png, + "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg", + ) + .to_string() } /// URL to the `.jpg` image of the `en_us` locale of the full card art, via `poro.steffo.eu`. @@ -66,37 +70,44 @@ impl CardArt { lazy_static! { static ref GET_JPG: Regex = Regex::new( r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P[^/]+)/(?P[^/]+)/img/cards/(?P.+)[.]png$"# - ).unwrap(); + ) + .unwrap(); } - GET_JPG.replace_all(&self.full_png, "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg").to_string() + GET_JPG + .replace_all( + &self.full_png, + "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg", + ) + .to_string() } } - #[cfg(test)] mod tests { use super::CardArt; #[test] fn deserialize() { - assert_eq!( - serde_json::de::from_str::<'static, CardArt>(r#"{"gameAbsolutePath": "https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png", "fullAbsolutePath": "https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png"}"#).unwrap(), - CardArt { - card_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png"), - full_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png"), - } - ); + assert_eq!(serde_json::de::from_str::<'static, CardArt>(r#"{"gameAbsolutePath": "https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png", "fullAbsolutePath": "https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png"}"#).unwrap(), CardArt { card_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png"), full_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png") }); } #[test] fn png_to_jpg() { let art = CardArt { card_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png"), - full_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png"), + full_png: String::from( + "https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png", + ), }; - assert_eq!(art.card_jpg(), "https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001.jpg"); - assert_eq!(art.full_jpg(), "https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001-full.jpg"); + assert_eq!( + art.card_jpg(), + "https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001.jpg" + ); + assert_eq!( + art.full_jpg(), + "https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001-full.jpg" + ); } -} \ No newline at end of file +} diff --git a/src/data/setbundle/card.rs b/src/data/setbundle/card.rs index 77c2a5f..0290146 100644 --- a/src/data/setbundle/card.rs +++ b/src/data/setbundle/card.rs @@ -169,6 +169,7 @@ mod tests { use super::*; #[test] + #[rustfmt::skip] fn deserialize_card() { assert_eq!( serde_json::de::from_str::<'static, Card>(r#" diff --git a/src/data/setbundle/keyword.rs b/src/data/setbundle/keyword.rs index 8afafe8..3a94ae3 100644 --- a/src/data/setbundle/keyword.rs +++ b/src/data/setbundle/keyword.rs @@ -342,12 +342,14 @@ impl CardKeyword { /// Returns [Option::None] if no matching [LocalizedCardKeyword] was found, for example for [CardKeyword::Unsupported] keywords. /// /// Equivalent to calling [LocalizedCardKeywordIndex::get]. - pub fn localized<'hm>(&self, hm: &'hm LocalizedCardKeywordIndex) -> Option<&'hm LocalizedCardKeyword> { + pub fn localized<'hm>( + &self, + hm: &'hm LocalizedCardKeywordIndex, + ) -> Option<&'hm LocalizedCardKeyword> { hm.get(self) } } - #[cfg(test)] mod tests { use super::CardKeyword; @@ -356,79 +358,202 @@ mod tests { ( $id:ident, $src:literal, $res:expr ) => { #[test] fn $id() { - assert_eq!(serde_json::de::from_str::<'static, CardKeyword>($src).unwrap(), $res); + assert_eq!( + serde_json::de::from_str::<'static, CardKeyword>($src).unwrap(), + $res + ); } - } + }; } - test_deserialization!(deserialize_spelloverwhelm, r#""SpellOverwhelm""#, CardKeyword::SpellOverwhelm); + test_deserialization!( + deserialize_spelloverwhelm, + r#""SpellOverwhelm""#, + CardKeyword::SpellOverwhelm + ); test_deserialization!(deserialize_burst, r#""Burst""#, CardKeyword::Burst); - test_deserialization!(deserialize_countdown, r#""Countdown""#, CardKeyword::Countdown); - test_deserialization!(deserialize_onplay, r#""PlaySkillMark""#, CardKeyword::OnPlay); - test_deserialization!(deserialize_landmark, r#""LandmarkVisualOnly""#, CardKeyword::Landmark); + test_deserialization!( + deserialize_countdown, + r#""Countdown""#, + CardKeyword::Countdown + ); + test_deserialization!( + deserialize_onplay, + r#""PlaySkillMark""#, + CardKeyword::OnPlay + ); + test_deserialization!( + deserialize_landmark, + r#""LandmarkVisualOnly""#, + CardKeyword::Landmark + ); test_deserialization!(deserialize_shurima, r#""Shurima""#, CardKeyword::Shurima); test_deserialization!(deserialize_attach, r#""Attach""#, CardKeyword::Attach); test_deserialization!(deserialize_noxus, r#""Noxus""#, CardKeyword::Noxus); test_deserialization!(deserialize_fleeting, r#""Fleeting""#, CardKeyword::Fleeting); - test_deserialization!(deserialize_clobbernoemptyslotrequirement, r#""ClobberNoEmptySlotRequirement""#, CardKeyword::ClobberNoEmptySlotRequirement); + test_deserialization!( + deserialize_clobbernoemptyslotrequirement, + r#""ClobberNoEmptySlotRequirement""#, + CardKeyword::ClobberNoEmptySlotRequirement + ); test_deserialization!(deserialize_nab, r#""Nab""#, CardKeyword::Nab); test_deserialization!(deserialize_focus, r#""Focus""#, CardKeyword::Focus); - test_deserialization!(deserialize_enlightened, r#""Enlightened""#, CardKeyword::Enlightened); + test_deserialization!( + deserialize_enlightened, + r#""Enlightened""#, + CardKeyword::Enlightened + ); test_deserialization!(deserialize_invoke, r#""Invoke""#, CardKeyword::Invoke); test_deserialization!(deserialize_boon, r#""Boon""#, CardKeyword::Boon); test_deserialization!(deserialize_trap, r#""Autoplay""#, CardKeyword::Trap); test_deserialization!(deserialize_drain, r#""Drain""#, CardKeyword::Drain); - test_deserialization!(deserialize_lastbreath, r#""LastBreath""#, CardKeyword::LastBreath); + test_deserialization!( + deserialize_lastbreath, + r#""LastBreath""#, + CardKeyword::LastBreath + ); test_deserialization!(deserialize_demacia, r#""Demacia""#, CardKeyword::Demacia); - test_deserialization!(deserialize_bandlecity, r#""BandleCity""#, CardKeyword::BandleCity); + test_deserialization!( + deserialize_bandlecity, + r#""BandleCity""#, + CardKeyword::BandleCity + ); test_deserialization!(deserialize_fast, r#""Fast""#, CardKeyword::Fast); - test_deserialization!(deserialize_bilgewater, r#""Bilgewater""#, CardKeyword::Bilgewater); - test_deserialization!(deserialize_runeterra, r#""Runeterra""#, CardKeyword::Runeterra); + test_deserialization!( + deserialize_bilgewater, + r#""Bilgewater""#, + CardKeyword::Bilgewater + ); + test_deserialization!( + deserialize_runeterra, + r#""Runeterra""#, + CardKeyword::Runeterra + ); test_deserialization!(deserialize_recall, r#""Recall""#, CardKeyword::Recall); test_deserialization!(deserialize_weakest, r#""Weakest""#, CardKeyword::Weakest); test_deserialization!(deserialize_support, r#""Support""#, CardKeyword::Support); test_deserialization!(deserialize_slow, r#""Slow""#, CardKeyword::Slow); - test_deserialization!(deserialize_obliterate, r#""Obliterate""#, CardKeyword::Obliterate); + test_deserialization!( + deserialize_obliterate, + r#""Obliterate""#, + CardKeyword::Obliterate + ); test_deserialization!(deserialize_imbue, r#""Imbue""#, CardKeyword::Imbue); test_deserialization!(deserialize_targon, r#""MtTargon""#, CardKeyword::Targon); - test_deserialization!(deserialize_shadowisles, r#""ShadowIsles""#, CardKeyword::ShadowIsles); - test_deserialization!(deserialize_auravisualfakekeyword, r#""AuraVisualFakeKeyword""#, CardKeyword::AuraVisualFakeKeyword); + test_deserialization!( + deserialize_shadowisles, + r#""ShadowIsles""#, + CardKeyword::ShadowIsles + ); + test_deserialization!( + deserialize_auravisualfakekeyword, + r#""AuraVisualFakeKeyword""#, + CardKeyword::AuraVisualFakeKeyword + ); test_deserialization!(deserialize_ionia, r#""Ionia""#, CardKeyword::Ionia); - test_deserialization!(deserialize_nightfall, r#""Nightfall""#, CardKeyword::Nightfall); - test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardKeyword::PiltoverZaun); + test_deserialization!( + deserialize_nightfall, + r#""Nightfall""#, + CardKeyword::Nightfall + ); + test_deserialization!( + deserialize_piltoverzaun, + r#""PiltoverZaun""#, + CardKeyword::PiltoverZaun + ); test_deserialization!(deserialize_attune, r#""Attune""#, CardKeyword::Attune); test_deserialization!(deserialize_daybreak, r#""Daybreak""#, CardKeyword::Daybreak); - test_deserialization!(deserialize_silenceindividualkeyword, r#""SilenceIndividualKeyword""#, CardKeyword::SilenceIndividualKeyword); + test_deserialization!( + deserialize_silenceindividualkeyword, + r#""SilenceIndividualKeyword""#, + CardKeyword::SilenceIndividualKeyword + ); test_deserialization!(deserialize_skill, r#""Skill""#, CardKeyword::Skill); test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder); - test_deserialization!(deserialize_doubleattack, r#""DoubleAttack""#, CardKeyword::DoubleAttack); - test_deserialization!(deserialize_vulnerable, r#""Vulnerable""#, CardKeyword::Vulnerable); + test_deserialization!( + deserialize_doubleattack, + r#""DoubleAttack""#, + CardKeyword::DoubleAttack + ); + test_deserialization!( + deserialize_vulnerable, + r#""Vulnerable""#, + CardKeyword::Vulnerable + ); test_deserialization!(deserialize_elusive, r#""Elusive""#, CardKeyword::Elusive); test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun); test_deserialization!(deserialize_fated, r#""Fated""#, CardKeyword::Fated); - test_deserialization!(deserialize_blockelusive, r#""BlockElusive""#, CardKeyword::BlockElusive); + test_deserialization!( + deserialize_blockelusive, + r#""BlockElusive""#, + CardKeyword::BlockElusive + ); test_deserialization!(deserialize_fury, r#""Fury""#, CardKeyword::Fury); test_deserialization!(deserialize_barrier, r#""Barrier""#, CardKeyword::Barrier); test_deserialization!(deserialize_immobile, r#""Immobile""#, CardKeyword::Immobile); test_deserialization!(deserialize_hallowed, r#""Hallowed""#, CardKeyword::Hallowed); test_deserialization!(deserialize_evolve, r#""Evolve""#, CardKeyword::Evolve); - test_deserialization!(deserialize_frostbite, r#""Frostbite""#, CardKeyword::Frostbite); - test_deserialization!(deserialize_overwhelm, r#""Overwhelm""#, CardKeyword::Overwhelm); - test_deserialization!(deserialize_quickattack, r#""QuickStrike""#, CardKeyword::QuickAttack); + test_deserialization!( + deserialize_frostbite, + r#""Frostbite""#, + CardKeyword::Frostbite + ); + test_deserialization!( + deserialize_overwhelm, + r#""Overwhelm""#, + CardKeyword::Overwhelm + ); + test_deserialization!( + deserialize_quickattack, + r#""QuickStrike""#, + CardKeyword::QuickAttack + ); test_deserialization!(deserialize_tough, r#""Tough""#, CardKeyword::Tough); - test_deserialization!(deserialize_regeneration, r#""Regeneration""#, CardKeyword::Regeneration); + test_deserialization!( + deserialize_regeneration, + r#""Regeneration""#, + CardKeyword::Regeneration + ); test_deserialization!(deserialize_silenced, r#""Silenced""#, CardKeyword::Silenced); - test_deserialization!(deserialize_spellshield, r#""SpellShield""#, CardKeyword::SpellShield); - test_deserialization!(deserialize_lifesteal, r#""Lifesteal""#, CardKeyword::Lifesteal); + test_deserialization!( + deserialize_spellshield, + r#""SpellShield""#, + CardKeyword::SpellShield + ); + test_deserialization!( + deserialize_lifesteal, + r#""Lifesteal""#, + CardKeyword::Lifesteal + ); test_deserialization!(deserialize_augment, r#""Augment""#, CardKeyword::Augment); test_deserialization!(deserialize_impact, r#""Impact""#, CardKeyword::Impact); test_deserialization!(deserialize_scout, r#""Scout""#, CardKeyword::Scout); - test_deserialization!(deserialize_ephemeral, r#""Ephemeral""#, CardKeyword::Ephemeral); + test_deserialization!( + deserialize_ephemeral, + r#""Ephemeral""#, + CardKeyword::Ephemeral + ); test_deserialization!(deserialize_lurk, r#""Lurker""#, CardKeyword::Lurk); - test_deserialization!(deserialize_formidable, r#""Formidable""#, CardKeyword::Formidable); - test_deserialization!(deserialize_challenger, r#""Challenger""#, CardKeyword::Challenger); + test_deserialization!( + deserialize_formidable, + r#""Formidable""#, + CardKeyword::Formidable + ); + test_deserialization!( + deserialize_challenger, + r#""Challenger""#, + CardKeyword::Challenger + ); test_deserialization!(deserialize_fearsome, r#""Fearsome""#, CardKeyword::Fearsome); - test_deserialization!(deserialize_cantblock, r#""CantBlock""#, CardKeyword::CantBlock); + test_deserialization!( + deserialize_cantblock, + r#""CantBlock""#, + CardKeyword::CantBlock + ); test_deserialization!(deserialize_deep, r#""Deep""#, CardKeyword::Deep); - test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardKeyword::Unsupported); + test_deserialization!( + deserialize_unsupported, + r#""Xyzzy""#, + CardKeyword::Unsupported + ); } diff --git a/src/data/setbundle/mod.rs b/src/data/setbundle/mod.rs index fd68893..77b4603 100644 --- a/src/data/setbundle/mod.rs +++ b/src/data/setbundle/mod.rs @@ -3,22 +3,21 @@ //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Set Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_set-bundles -use std::fs::File; -use std::path::Path; use super::anybundle::metadata::BundleMetadata; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; +use std::fs::File; +use std::path::Path; -pub mod card; pub mod art; -pub mod r#type; +pub mod card; +pub mod keyword; pub mod rarity; pub mod region; pub mod set; pub mod speed; -pub mod keyword; pub mod subtype; pub mod supertype; - +pub mod r#type; /// A parsed [Data Dragon] [Set Bundle]. /// @@ -35,41 +34,38 @@ pub struct SetBundle { pub name: String, } - impl SetBundle { /// Load a Set Bundle directory to create a [SetBundle] instance. pub fn load(bundle_path: &Path) -> LoadingResult { - let metadata = BundleMetadata::load( - &bundle_path - .join("metadata.json") - )?; + let metadata = BundleMetadata::load(&bundle_path.join("metadata.json"))?; - let locale = metadata.locale() - .ok_or(LoadingError::GettingLocale)?; + let locale = metadata.locale().ok_or(LoadingError::GettingLocale)?; - let name = bundle_path.file_name() + let name = bundle_path + .file_name() .ok_or(LoadingError::GettingBundleName)?; let data_path = { 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.to_str() + let name = name + .to_str() .ok_or(LoadingError::ConvertingBundleName)? .to_string(); - let cards = File::open(data_path) - .map_err(LoadingError::OpeningFile)?; + let cards = File::open(data_path).map_err(LoadingError::OpeningFile)?; let cards = serde_json::de::from_reader::>(cards) .map_err(LoadingError::Deserializing)?; - Ok(SetBundle {metadata, cards, name}) + Ok(SetBundle { + metadata, + cards, + name, + }) } } diff --git a/src/data/setbundle/rarity.rs b/src/data/setbundle/rarity.rs index 7a0c0aa..86fd56a 100644 --- a/src/data/setbundle/rarity.rs +++ b/src/data/setbundle/rarity.rs @@ -27,12 +27,14 @@ impl CardRarity { /// Returns [Option::None] if no matching [LocalizedCardRarity] was found, for example rarities missing from the index. /// /// Equivalent to calling [LocalizedCardRarityIndex::get]. - pub fn localized<'hm>(&self, hm: &'hm LocalizedCardRarityIndex) -> Option<&'hm LocalizedCardRarity> { + pub fn localized<'hm>( + &self, + hm: &'hm LocalizedCardRarityIndex, + ) -> Option<&'hm LocalizedCardRarity> { hm.get(self) } } - #[cfg(test)] mod tests { use super::CardRarity; @@ -41,9 +43,12 @@ mod tests { ( $id:ident, $src:literal, $res:expr ) => { #[test] fn $id() { - assert_eq!(serde_json::de::from_str::<'static, CardRarity>($src).unwrap(), $res); + assert_eq!( + serde_json::de::from_str::<'static, CardRarity>($src).unwrap(), + $res + ); } - } + }; } test_deserialization!(deserialize_none, r#""None""#, CardRarity::None); diff --git a/src/data/setbundle/region.rs b/src/data/setbundle/region.rs index ee03295..9c26b7a 100644 --- a/src/data/setbundle/region.rs +++ b/src/data/setbundle/region.rs @@ -55,6 +55,7 @@ impl CardRegion { #[cfg(test)] +#[rustfmt::skip] mod tests { use super::CardRegion; diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index b9892e2..2eae6f3 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -52,7 +52,6 @@ impl CardSet { } } - #[cfg(test)] mod tests { use super::CardSet; @@ -61,9 +60,12 @@ mod tests { ( $id:ident, $src:literal, $res:expr ) => { #[test] fn $id() { - assert_eq!(serde_json::de::from_str::<'static, CardSet>($src).unwrap(), $res); + assert_eq!( + serde_json::de::from_str::<'static, CardSet>($src).unwrap(), + $res + ); } - } + }; } test_deserialization!(deserialize_set1, r#""Set1""#, CardSet::Foundations); diff --git a/src/data/setbundle/speed.rs b/src/data/setbundle/speed.rs index 49e02c2..8b665c8 100644 --- a/src/data/setbundle/speed.rs +++ b/src/data/setbundle/speed.rs @@ -22,12 +22,14 @@ impl SpellSpeed { /// Returns [Option::None] if no matching [LocalizedSpellSpeed] was found, for example spell speeds missing from the index. /// /// Equivalent to calling [LocalizedSpellSpeedIndex::get]. - pub fn localized<'hm>(&self, hm: &'hm LocalizedSpellSpeedIndex) -> Option<&'hm LocalizedSpellSpeed> { + pub fn localized<'hm>( + &self, + hm: &'hm LocalizedSpellSpeedIndex, + ) -> Option<&'hm LocalizedSpellSpeed> { hm.get(self) } } - #[cfg(test)] mod tests { use super::SpellSpeed; @@ -36,9 +38,12 @@ mod tests { ( $id:ident, $src:literal, $res:expr ) => { #[test] fn $id() { - assert_eq!(serde_json::de::from_str::<'static, SpellSpeed>($src).unwrap(), $res); + assert_eq!( + serde_json::de::from_str::<'static, SpellSpeed>($src).unwrap(), + $res + ); } - } + }; } test_deserialization!(deserialize_none, r#""""#, SpellSpeed::None); diff --git a/src/data/setbundle/type.rs b/src/data/setbundle/type.rs index 3f643a4..fe97231 100644 --- a/src/data/setbundle/type.rs +++ b/src/data/setbundle/type.rs @@ -1,6 +1,5 @@ //! Module defining [CardType]. - /// A possible [Card](super::card::Card) type. /// /// Since more types might be added in the future, as it happened with landmarks, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). @@ -31,7 +30,6 @@ pub enum CardType { Unsupported, } - impl From<&CardType> for String { fn from(r#type: &CardType) -> Self { match r#type { @@ -45,7 +43,6 @@ impl From<&CardType> for String { } } - #[cfg(test)] mod tests { use super::CardType; @@ -54,9 +51,12 @@ mod tests { ( $id:ident, $src:literal, $res:expr ) => { #[test] fn $id() { - assert_eq!(serde_json::de::from_str::<'static, CardType>($src).unwrap(), $res); + assert_eq!( + serde_json::de::from_str::<'static, CardType>($src).unwrap(), + $res + ); } - } + }; } test_deserialization!(deserialize_spell, r#""Spell""#, CardType::Spell); @@ -64,4 +64,4 @@ mod tests { test_deserialization!(deserialize_ability, r#""Ability""#, CardType::Ability); test_deserialization!(deserialize_landmark, r#""Landmark""#, CardType::Landmark); test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardType::Unsupported); -} \ No newline at end of file +} diff --git a/src/matrix/mod.rs b/src/matrix/mod.rs index 419eee7..d25c84d 100644 --- a/src/matrix/mod.rs +++ b/src/matrix/mod.rs @@ -1,4 +1,3 @@ //! Module providing utilities to be used in the `patched_porobot_matrix` executable target. //! //! While adding new features to this module, remember that binaries [can only access the public API of the crate](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries), as they considered a separate crate from the rest of the project. - diff --git a/src/search/cardsearch.rs b/src/search/cardsearch.rs index 999c166..95a5838 100644 --- a/src/search/cardsearch.rs +++ b/src/search/cardsearch.rs @@ -1,14 +1,13 @@ //! Module defining a search engine to find [Card]s. -use tantivy::{Document, Index, IndexReader, IndexWriter}; +use crate::data::corebundle::globals::LocalizedGlobalsIndexes; +use crate::data::setbundle::card::{Card, CardIndex}; +use itertools::Itertools; use tantivy::collector::TopDocs; use tantivy::query::{QueryParser, QueryParserError}; use tantivy::schema::{Field, NumericOptions, Schema, TextOptions}; use tantivy::tokenizer::TextAnalyzer; -use itertools::Itertools; -use crate::data::corebundle::globals::LocalizedGlobalsIndexes; -use crate::data::setbundle::card::{Card, CardIndex}; - +use tantivy::{Document, Index, IndexReader, IndexWriter}; /// The search engine. /// @@ -29,10 +28,9 @@ pub struct CardSearchEngine { pub globals: LocalizedGlobalsIndexes, /// Cards searchable in the search engine. - pub cards: CardIndex + pub cards: CardIndex, } - impl CardSearchEngine { /// Create the [tantivy::tokenizer::TextAnalyzer] for card text. /// @@ -40,8 +38,7 @@ impl CardSearchEngine { fn tokenizer() -> TextAnalyzer { use tantivy::tokenizer::*; - TextAnalyzer::from(SimpleTokenizer) - .filter(LowerCaser) + TextAnalyzer::from(SimpleTokenizer).filter(LowerCaser) } /// Create the [tantivy::schema::TextOptions] for card codes. @@ -54,9 +51,10 @@ impl CardSearchEngine { use tantivy::schema::*; TextOptions::default() - .set_indexing_options(TextFieldIndexing::default() - .set_tokenizer("card") - .set_index_option(IndexRecordOption::Basic) + .set_indexing_options( + TextFieldIndexing::default() + .set_tokenizer("card") + .set_index_option(IndexRecordOption::Basic), ) .set_stored() .set_fast() @@ -70,11 +68,11 @@ impl CardSearchEngine { fn options_keyword() -> TextOptions { use tantivy::schema::*; - TextOptions::default() - .set_indexing_options(TextFieldIndexing::default() + TextOptions::default().set_indexing_options( + TextFieldIndexing::default() .set_tokenizer("card") - .set_index_option(IndexRecordOption::Basic) - ) + .set_index_option(IndexRecordOption::Basic), + ) } /// Create the [tantivy::schema::TextOptions] for card text fields. @@ -85,11 +83,11 @@ impl CardSearchEngine { fn options_text() -> TextOptions { use tantivy::schema::*; - TextOptions::default() - .set_indexing_options(TextFieldIndexing::default() + TextOptions::default().set_indexing_options( + TextFieldIndexing::default() .set_tokenizer("card") - .set_index_option(IndexRecordOption::WithFreqsAndPositions) - ) + .set_index_option(IndexRecordOption::WithFreqsAndPositions), + ) } /// Create the [tantivy::schema::NumericOptions] for card numeric fields. @@ -99,8 +97,7 @@ impl CardSearchEngine { fn options_number() -> NumericOptions { use tantivy::schema::*; - NumericOptions::default() - .set_indexed() + NumericOptions::default().set_indexed() } /// Create the [Schema] for the search engine. @@ -163,32 +160,66 @@ impl CardSearchEngine { /// Create a [CardSchemaFields] object from the given schema. fn schema_fields(schema: &Schema) -> CardSchemaFields { CardSchemaFields { - code: schema.get_field("code").expect("schema to have a 'code' field"), - name: schema.get_field("name").expect("schema to have a 'name' field"), - r#type: schema.get_field("type").expect("schema to have a 'type' field"), - set: schema.get_field("set").expect("schema to have a 'set' field"), - rarity: schema.get_field("rarity").expect("schema to have a 'rarity' field"), - collectible: schema.get_field("collectible").expect("schema to have a 'collectible' field"), - regions: schema.get_field("regions").expect("schema to have a 'regions' field"), - attack: schema.get_field("attack").expect("schema to have a 'attack' field"), - cost: schema.get_field("cost").expect("schema to have a 'cost' field"), - health: schema.get_field("health").expect("schema to have a 'health' field"), - spellspeed: schema.get_field("spellspeed").expect("schema to have a 'spellspeed' field"), - keywords: schema.get_field("keywords").expect("schema to have a 'keywords' field"), - description: schema.get_field("description").expect("schema to have a 'description' field"), - levelup: schema.get_field("levelup").expect("schema to have a 'levelup' field"), - flavor: schema.get_field("flavor").expect("schema to have a 'flavor' field"), - artist: schema.get_field("artist").expect("schema to have a 'artist' field"), - subtypes: schema.get_field("subtypes").expect("schema to have a 'subtypes' field"), - supertype: schema.get_field("supertype").expect("schema to have a 'supertype' field"), + code: schema + .get_field("code") + .expect("schema to have a 'code' field"), + name: schema + .get_field("name") + .expect("schema to have a 'name' field"), + r#type: schema + .get_field("type") + .expect("schema to have a 'type' field"), + set: schema + .get_field("set") + .expect("schema to have a 'set' field"), + rarity: schema + .get_field("rarity") + .expect("schema to have a 'rarity' field"), + collectible: schema + .get_field("collectible") + .expect("schema to have a 'collectible' field"), + regions: schema + .get_field("regions") + .expect("schema to have a 'regions' field"), + attack: schema + .get_field("attack") + .expect("schema to have a 'attack' field"), + cost: schema + .get_field("cost") + .expect("schema to have a 'cost' field"), + health: schema + .get_field("health") + .expect("schema to have a 'health' field"), + spellspeed: schema + .get_field("spellspeed") + .expect("schema to have a 'spellspeed' field"), + keywords: schema + .get_field("keywords") + .expect("schema to have a 'keywords' field"), + description: schema + .get_field("description") + .expect("schema to have a 'description' field"), + levelup: schema + .get_field("levelup") + .expect("schema to have a 'levelup' field"), + flavor: schema + .get_field("flavor") + .expect("schema to have a 'flavor' field"), + artist: schema + .get_field("artist") + .expect("schema to have a 'artist' field"), + subtypes: schema + .get_field("subtypes") + .expect("schema to have a 'subtypes' field"), + supertype: schema + .get_field("supertype") + .expect("schema to have a 'supertype' field"), } } /// Build [in RAM](Index::create_in_ram) the [Index] of the search engine. fn index() -> Index { - Index::create_in_ram( - Self::schema() - ) + Index::create_in_ram(Self::schema()) } /// Build a [IndexWriter] with the optimal configuration for the search engine. @@ -210,7 +241,11 @@ impl CardSearchEngine { } /// Create a [Document] from a [Card]. - fn document(fields: &CardSchemaFields, globals: &LocalizedGlobalsIndexes, card: Card) -> Document { + fn document( + fields: &CardSchemaFields, + globals: &LocalizedGlobalsIndexes, + card: Card, + ) -> Document { use tantivy::doc; doc!( @@ -268,7 +303,7 @@ impl CardSearchEngine { fields.artist, fields.subtypes, fields.supertype, - ] + ], ); parser.set_conjunction_by_default(); parser.set_field_boost(fields.code, 3.0); @@ -288,16 +323,24 @@ impl CardSearchEngine { let mut writer = Self::writer(&index); for card in cards.values() { let document = Self::document(&fields, &globals, card.clone()); - writer.add_document(document) + writer + .add_document(document) .expect("IndexWriter threads to not panic or die before adding a document"); - }; - writer.commit() + } + writer + .commit() .expect("IndexWriter threads to not panic or die before commit"); let parser = Self::parser(&index, fields); let reader = Self::reader(&index); - Self {index, reader, parser, globals, cards} + Self { + index, + reader, + parser, + globals, + cards, + } } /// Perform a query on the search engine. @@ -306,13 +349,18 @@ impl CardSearchEngine { let query = self.parser.parse_query(input)?; - let search = searcher.search(&*query, &TopDocs::with_limit(top)) + let search = searcher + .search(&*query, &TopDocs::with_limit(top)) .expect("Searcher::search to never fail"); - let f_code = self.index.schema().get_field("code") + let f_code = self + .index + .schema() + .get_field("code") .expect("schema to have a 'code' field"); - let results = search.iter() + let results = search + .iter() .filter_map(|(_score, address)| searcher.doc(address.to_owned()).ok()) .filter_map(|doc| doc.get_first(f_code).cloned()) .filter_map(|field| field.as_text().map(String::from)) @@ -323,7 +371,6 @@ impl CardSearchEngine { } } - /// Struct containing all retrieved [CardSearchEngine] [Field]s. /// /// This makes it easier to pass them around without having to re-fetch them every time they are used. diff --git a/src/telegram/display.rs b/src/telegram/display.rs index 36c5a84..c1ece4f 100644 --- a/src/telegram/display.rs +++ b/src/telegram/display.rs @@ -2,45 +2,35 @@ //! //! [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style -use itertools::Itertools; -use teloxide::utils::html::escape; -use crate::data::setbundle::card::Card; -use crate::data::setbundle::r#type::CardType; use crate::data::corebundle::globals::LocalizedGlobalsIndexes; use crate::data::corebundle::keyword::LocalizedCardKeywordIndex; use crate::data::corebundle::region::LocalizedCardRegionIndex; use crate::data::corebundle::set::LocalizedCardSetIndex; +use crate::data::setbundle::card::Card; use crate::data::setbundle::keyword::CardKeyword; +use crate::data::setbundle::r#type::CardType; use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::set::CardSet; use crate::data::setbundle::subtype::CardSubtype; use crate::data::setbundle::supertype::CardSupertype; - +use itertools::Itertools; +use teloxide::utils::html::escape; /// Render a [Card] in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String { - let title = format!( - "{}\n", - escape(&card.name), - ); + let title = format!("{}\n", escape(&card.name),); let stats = match &card.r#type { - CardType::Spell => format!( - "{} mana\n\n", - escape(&card.cost.to_string()), - ), + CardType::Spell => format!("{} mana\n\n", escape(&card.cost.to_string()),), CardType::Unit => format!( "{} mana {}|{}\n\n", escape(&card.cost.to_string()), escape(&card.attack.to_string()), escape(&card.health.to_string()), ), - CardType::Landmark => format!( - "{} mana\n\n", - &card.cost - ), + CardType::Landmark => format!("{} mana\n\n", &card.cost), _ => "".to_string(), }; @@ -55,60 +45,50 @@ pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String { let description = display_description(&card.localized_description_text); let levelup = display_levelup(&card.localized_levelup_text); - let flavor = format!( - "{}\n", - escape(&card.localized_flavor_text) - ); + let flavor = format!("{}\n", escape(&card.localized_flavor_text)); let artist = format!( r#"Illustration by {}"#, - &card.main_art().expect("Card to have at least one illustration").full_png, + &card + .main_art() + .expect("Card to have at least one illustration") + .full_png, escape(&card.artist_name) ); format!( "{}{}{}{}{}{}-----\n{}{}", - &title, - &breadcrumbs, - &keywords, - &stats, - &description, - &levelup, - &flavor, - &artist, + &title, &breadcrumbs, &keywords, &stats, &description, &levelup, &flavor, &artist, ) } - /// Render a [CardSet] in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String { format!( "{}", - set - .localized(hm) + set.localized(hm) .map(|o| format!("{}", escape(&o.name))) .unwrap_or_else(|| "Unknown".to_string()) ) } - /// Render a slice of [CardRegion]s in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style fn display_regions(regions: &[CardRegion], hm: &LocalizedCardRegionIndex) -> String { regions .iter() - .map(|region| region - .localized(hm) - .map(|o| format!("{}", escape(&o.name))) - .unwrap_or_else(|| "Unknown".to_string()) - ) + .map(|region| { + region + .localized(hm) + .map(|o| format!("{}", escape(&o.name))) + .unwrap_or_else(|| "Unknown".to_string()) + }) .join(", ") } - /// Render the [CardType], the [CardSupertype] and the [CardSubtype]s in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style @@ -116,32 +96,24 @@ fn display_types(r#type: &CardType, supertype: &CardSupertype, subtypes: &[CardS let mut result = String::new(); if supertype != "" { - result.push_str(&*format!( - "{} › ", - escape(&supertype), - )); + result.push_str(&*format!("{} › ", escape(&supertype),)); }; - result.push_str(&*format!( - "{}", - escape(&*String::from(r#type)), - )); + result.push_str(&*format!("{}", escape(&*String::from(r#type)),)); if subtypes.len() > 0 { - result.push_str( - &*format!( - " › {}", - subtypes.iter() - .map(|subtype| format!("{}", escape(&subtype))) - .join(", ") - ) - ) + result.push_str(&*format!( + " › {}", + subtypes + .iter() + .map(|subtype| format!("{}", escape(&subtype))) + .join(", ") + )) } result } - /// Render a slice of [CardKeyword]s in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style @@ -153,35 +125,29 @@ fn display_keywords(keywords: &[CardKeyword], hm: &LocalizedCardKeywordIndex) -> .map(|keyword| keyword .localized(hm) .map(|o| format!("[{}]", escape(&o.name))) - .unwrap_or_else(|| "Unknown".to_string()) - ) + .unwrap_or_else(|| "Unknown".to_string())) .join(" ") ) - } - /// Render a [Card::localized_description_text] in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style fn display_description(description: &String) -> String { if description == "" { "".to_string() - } - else { + } else { format!("{}\n\n", escape(&description)) } } - /// Render a [Card::localized_levelup_text] in [Telegram Bot HTML]. /// /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style fn display_levelup(levelup: &String) -> String { if levelup == "" { "".to_string() - } - else { + } else { format!("Level up: {}\n\n", escape(&levelup)) } -} \ No newline at end of file +} diff --git a/src/telegram/handler.rs b/src/telegram/handler.rs index 8569751..2954f81 100644 --- a/src/telegram/handler.rs +++ b/src/telegram/handler.rs @@ -1,18 +1,19 @@ //! Module providing handlers for @patchedporobot on Telegram. +use crate::search::cardsearch::CardSearchEngine; +use crate::telegram::inline::card_to_inlinequeryresult; use itertools::Itertools; use log::*; use teloxide::dispatching::DpHandlerDescription; use teloxide::payloads::{AnswerInlineQuery, SendMessage}; -use teloxide::requests::{JsonRequest, ResponseResult}; use teloxide::prelude::*; +use teloxide::requests::{JsonRequest, ResponseResult}; use teloxide::types::{ParseMode, Recipient}; -use crate::search::cardsearch::CardSearchEngine; -use crate::telegram::inline::card_to_inlinequeryresult; - /// Handle inline queries by searching cards on the [CardSearchEngine]. -pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, DependencyMap, ResponseResult<()>, DpHandlerDescription> { +pub fn inline_query_handler( + engine: CardSearchEngine, +) -> Handler<'static, DependencyMap, ResponseResult<()>, DpHandlerDescription> { Update::filter_inline_query().chain(dptree::endpoint(move |query: InlineQuery, bot: Bot| { info!("Handling inline query: `{}`", &query.query); @@ -27,7 +28,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend next_offset: None, switch_pm_text: Some("How to search cards".to_string()), switch_pm_parameter: Some("err-no-query".to_string()), - } + }; } debug!("Querying the search engine..."); @@ -43,7 +44,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend next_offset: None, switch_pm_text: Some("Invalid query syntax".to_string()), switch_pm_parameter: Some("err-invalid-query".to_string()), - } + }; } let results = results.unwrap(); @@ -58,7 +59,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend next_offset: None, switch_pm_text: Some("No cards found".to_string()), switch_pm_parameter: Some("err-no-results".to_string()), - } + }; } debug!("Found {} cards.", &len); @@ -73,7 +74,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend next_offset: None, switch_pm_text: None, switch_pm_parameter: None, - } + }; }; async move { @@ -88,7 +89,6 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend })) } - const WELCOME_MESSAGE: &'static str = r#" 👋 Hi! I'm a robotic poro who can search for Legends of Runeterra cards to send them in chats! @@ -107,9 +107,9 @@ Have a fun time searching! @patchedporobot isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc. "#; - /// Handle all messages by replying with the help text. -pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>, DpHandlerDescription> { +pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>, DpHandlerDescription> +{ Update::filter_message().chain(dptree::endpoint(move |message: Message, bot: Bot| { info!("Handling private message: `{:?}`", &message.text()); @@ -123,7 +123,7 @@ pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>, protect_content: None, reply_to_message_id: None, allow_sending_without_reply: None, - reply_markup: None + reply_markup: None, }; async move { @@ -136,4 +136,4 @@ pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>, respond(()) } })) -} \ No newline at end of file +} diff --git a/src/telegram/inline.rs b/src/telegram/inline.rs index 7b72f8f..dba92e4 100644 --- a/src/telegram/inline.rs +++ b/src/telegram/inline.rs @@ -2,14 +2,16 @@ //! //! [inline mode]: https://core.telegram.org/bots/api#inline-mode -use teloxide::types::{InlineQueryResult, InlineQueryResultPhoto, ParseMode}; use crate::data::corebundle::globals::LocalizedGlobalsIndexes; use crate::data::setbundle::card::Card; use crate::telegram::display::display_card; - +use teloxide::types::{InlineQueryResult, InlineQueryResultPhoto, ParseMode}; /// Converts a [Card] into a [InlineQueryResult]. -pub fn card_to_inlinequeryresult(globals: &LocalizedGlobalsIndexes, card: &Card) -> InlineQueryResult { +pub fn card_to_inlinequeryresult( + globals: &LocalizedGlobalsIndexes, + card: &Card, +) -> InlineQueryResult { InlineQueryResult::Photo(InlineQueryResultPhoto { id: card.code.to_owned(), title: Some(card.name.to_owned()), @@ -18,16 +20,17 @@ pub fn card_to_inlinequeryresult(globals: &LocalizedGlobalsIndexes, card: &Card) photo_url: card .main_art() .expect("Card to have at least one illustration") - .card_jpg().parse() + .card_jpg() + .parse() .expect("Card to have a valid card_jpg URL"), thumb_url: card .main_art() .expect("Card to have at least one illustration") - .card_jpg().parse() + .card_jpg() + .parse() .expect("Card to have a valid card_jpg URL"), photo_width: Some(680), photo_height: Some(1024), - description: None, caption_entities: None, reply_markup: None, diff --git a/src/telegram/main.rs b/src/telegram/main.rs index c902d74..ddf0add 100644 --- a/src/telegram/main.rs +++ b/src/telegram/main.rs @@ -1,13 +1,13 @@ //! This module defines the [`main`] function for [`patched_porobot_telegram`]. -use std::path::PathBuf; -use log::*; -use crate::data::setbundle::card::{Card, CardIndex}; -use crate::data::corebundle::CoreBundle; -use crate::data::setbundle::SetBundle; 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::search::cardsearch::CardSearchEngine; use crate::telegram::handler::{inline_query_handler, message_handler}; +use log::*; +use std::path::PathBuf; use teloxide::prelude::*; /// The main function that [`patched_porobot_telegram`] should run when it's started. @@ -16,13 +16,20 @@ pub async fn main() { debug!("Logger initialized successfully!"); debug!("Loading bundles..."); - let core = CoreBundle::load(&*PathBuf::from("./data/core-en_us")).expect("to be able to load `core-en_us` bundle"); - let set1 = SetBundle::load(&*PathBuf::from("./data/set1-en_us")).expect("to be able to load `set1-en_us` bundle"); - let set2 = SetBundle::load(&*PathBuf::from("./data/set2-en_us")).expect("to be able to load `set2-en_us` bundle"); - let set3 = SetBundle::load(&*PathBuf::from("./data/set3-en_us")).expect("to be able to load `set3-en_us` bundle"); - let set4 = SetBundle::load(&*PathBuf::from("./data/set4-en_us")).expect("to be able to load `set4-en_us` bundle"); - let set5 = SetBundle::load(&*PathBuf::from("./data/set5-en_us")).expect("to be able to load `set5-en_us` bundle"); - let set6 = SetBundle::load(&*PathBuf::from("./data/set6-en_us")).expect("to be able to load `set6-en_us` bundle"); + let core = CoreBundle::load(&*PathBuf::from("./data/core-en_us")) + .expect("to be able to load `core-en_us` bundle"); + let set1 = SetBundle::load(&*PathBuf::from("./data/set1-en_us")) + .expect("to be able to load `set1-en_us` bundle"); + let set2 = SetBundle::load(&*PathBuf::from("./data/set2-en_us")) + .expect("to be able to load `set2-en_us` bundle"); + let set3 = SetBundle::load(&*PathBuf::from("./data/set3-en_us")) + .expect("to be able to load `set3-en_us` bundle"); + let set4 = SetBundle::load(&*PathBuf::from("./data/set4-en_us")) + .expect("to be able to load `set4-en_us` bundle"); + let set5 = SetBundle::load(&*PathBuf::from("./data/set5-en_us")) + .expect("to be able to load `set5-en_us` bundle"); + let set6 = SetBundle::load(&*PathBuf::from("./data/set6-en_us")) + .expect("to be able to load `set6-en_us` bundle"); debug!("Loaded all bundles!"); debug!("Indexing globals..."); @@ -31,13 +38,9 @@ pub async fn main() { debug!("Indexing cards..."); let cards: Vec = [ - set1.cards, - set2.cards, - set3.cards, - set4.cards, - set5.cards, - set6.cards - ].concat(); + set1.cards, set2.cards, set3.cards, set4.cards, set5.cards, set6.cards, + ] + .concat(); let mut index = CardIndex::new(); for card in cards { @@ -52,7 +55,11 @@ pub async fn main() { debug!("Creating Telegram bot with parameters from the environment..."); let bot = Bot::from_env(); - let me = bot.get_me().send().await.expect("Telegram bot parameters to be valid"); + let me = bot + .get_me() + .send() + .await + .expect("Telegram bot parameters to be valid"); debug!("Created Telegram bot!"); debug!("Creating handlers..."); @@ -61,10 +68,13 @@ pub async fn main() { .branch(message_handler()); debug!("Created handlers!"); - info!("@{} is ready!", &me.username.as_ref().expect("bot to have an username")); + info!( + "@{} is ready!", + &me.username.as_ref().expect("bot to have an username") + ); Dispatcher::builder(bot, handler) .enable_ctrlc_handler() .build() .dispatch() .await; -} \ No newline at end of file +} diff --git a/src/telegram/mod.rs b/src/telegram/mod.rs index acc6877..1723079 100644 --- a/src/telegram/mod.rs +++ b/src/telegram/mod.rs @@ -3,6 +3,6 @@ //! While adding new features to this module, remember that binaries [can only access the public API of the crate](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries), as they considered a separate crate from the rest of the project. pub mod display; -pub mod inline; pub mod handler; +pub mod inline; pub mod main;