1
Fork 0
mirror of https://github.com/Steffo99/patched-porobot.git synced 2024-12-22 09:34:21 +00:00

Configure and run cargo fmt

This commit is contained in:
Steffo 2022-08-13 04:28:46 +00:00 committed by GitHub
parent f954588c34
commit a2cc2cc4c0
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 484 additions and 351 deletions

0
rustfmt.toml Normal file
View file

View file

@ -77,7 +77,6 @@
#![doc(html_logo_url = "https://raw.githubusercontent.com/Steffo99/patched-porobot/main/icon.png")] #![doc(html_logo_url = "https://raw.githubusercontent.com/Steffo99/patched-porobot/main/icon.png")]
#[doc(hidden)] #[doc(hidden)]
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {

View file

@ -2,9 +2,9 @@
//! //!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
/// A parsed `metadata.json` file from a Data Dragon Bundle. /// A parsed `metadata.json` file from a Data Dragon Bundle.
/// ///
@ -24,17 +24,15 @@ pub struct BundleMetadata {
/// [Vec] of locales included in the bundle. /// [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. /// The specification defines that there can be multiple, but currently I've never seen more (or less) than one.
pub locales: Vec<String> pub locales: Vec<String>,
} }
impl BundleMetadata { impl BundleMetadata {
/// Load a `metadata.json` file to create a [BundleMetadata] instance. /// Load a `metadata.json` file to create a [BundleMetadata] instance.
pub fn load(path: &Path) -> LoadingResult<Self> { pub fn load(path: &Path) -> LoadingResult<Self> {
let file = File::open(path) let file = File::open(path).map_err(LoadingError::OpeningFile)?;
.map_err(LoadingError::OpeningFile)?; let data =
let data = serde_json::de::from_reader::<File, Self>(file) serde_json::de::from_reader::<File, Self>(file).map_err(LoadingError::Deserializing)?;
.map_err(LoadingError::Deserializing)?;
Ok(data) Ok(data)
} }
@ -46,7 +44,6 @@ impl BundleMetadata {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -54,13 +51,16 @@ mod tests {
#[test] #[test]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, BundleMetadata>(r#" serde_json::de::from_str::<'static, BundleMetadata>(
r#"
{ {
"locales": [ "locales": [
"en_us" "en_us"
] ]
} }
"#).unwrap(), "#
)
.unwrap(),
BundleMetadata { BundleMetadata {
locales: vec!["en_us".to_string()] locales: vec!["en_us".to_string()]
} }

View file

@ -2,7 +2,6 @@
//! //!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
/// An error that occoured while loading a Data Dragon Bundle. /// An error that occoured while loading a Data Dragon Bundle.
#[derive(Debug)] #[derive(Debug)]
pub enum LoadingError { pub enum LoadingError {

View file

@ -1,14 +1,14 @@
//! Module defining structs representing data contained in `globals.json` files. //! 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::fs::File;
use std::path::Path; 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]. /// A parsed `globals.json` file from a [Data Dragon] [Core Bundle].
/// ///
@ -68,19 +68,16 @@ pub struct LocalizedGlobalsIndexes {
pub sets: LocalizedCardSetIndex, pub sets: LocalizedCardSetIndex,
} }
impl LocalizedGlobalsVecs { impl LocalizedGlobalsVecs {
/// Load a `globals.json` file to create a [LocalizedGlobalsVecs] instance. /// Load a `globals.json` file to create a [LocalizedGlobalsVecs] instance.
pub fn load(path: &Path) -> LoadingResult<Self> { pub fn load(path: &Path) -> LoadingResult<Self> {
let file = File::open(path) let file = File::open(path).map_err(LoadingError::OpeningFile)?;
.map_err(LoadingError::OpeningFile)?; let data =
let data = serde_json::de::from_reader::<File, Self>(file) serde_json::de::from_reader::<File, Self>(file).map_err(LoadingError::Deserializing)?;
.map_err(LoadingError::Deserializing)?;
Ok(data) Ok(data)
} }
} }
impl From<LocalizedGlobalsVecs> for LocalizedGlobalsIndexes { impl From<LocalizedGlobalsVecs> for LocalizedGlobalsIndexes {
fn from(o: LocalizedGlobalsVecs) -> Self { fn from(o: LocalizedGlobalsVecs) -> Self {
Self { Self {
@ -130,7 +127,6 @@ impl From<LocalizedGlobalsVecs> for LocalizedGlobalsIndexes {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::LocalizedGlobalsVecs; use super::LocalizedGlobalsVecs;
@ -149,7 +145,8 @@ mod tests {
#[test] #[test]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedGlobalsVecs>(r#" serde_json::de::from_str::<'static, LocalizedGlobalsVecs>(
r#"
{ {
"vocabTerms": [ "vocabTerms": [
{ {
@ -193,50 +190,10 @@ mod tests {
} }
] ]
} }
"#).unwrap(), "#
LocalizedGlobalsVecs { )
vocab_terms: vec![ .unwrap(),
LocalizedVocabTerm { 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() }] }
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(),
}
]
}
) )
} }
} }

View file

@ -28,6 +28,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[rustfmt::skip]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedCardKeyword>(r#" serde_json::de::from_str::<'static, LocalizedCardKeyword>(r#"

View file

@ -3,18 +3,17 @@
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
//! [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles //! [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles
use std::path::Path;
use super::anybundle::metadata::BundleMetadata; use super::anybundle::metadata::BundleMetadata;
use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
use std::path::Path;
pub mod globals; pub mod globals;
pub mod vocabterm;
pub mod keyword; pub mod keyword;
pub mod region;
pub mod speed;
pub mod rarity; pub mod rarity;
pub mod region;
pub mod set; pub mod set;
pub mod speed;
pub mod vocabterm;
/// A parsed [Data Dragon] [Core Bundle]. /// A parsed [Data Dragon] [Core Bundle].
/// ///
@ -32,23 +31,19 @@ pub struct CoreBundle {
pub globals: globals::LocalizedGlobalsVecs, pub globals: globals::LocalizedGlobalsVecs,
} }
impl CoreBundle { impl CoreBundle {
/// Load a Core Bundle directory to create a [CoreBundle] instance. /// Load a Core Bundle directory to create a [CoreBundle] instance.
pub fn load(bundle_path: &Path) -> LoadingResult<Self> { pub fn load(bundle_path: &Path) -> LoadingResult<Self> {
let metadata = BundleMetadata::load( let metadata = BundleMetadata::load(&bundle_path.join("metadata.json"))?;
&bundle_path
.join("metadata.json")
)?;
let name = bundle_path.file_name() let name = bundle_path
.file_name()
.ok_or(LoadingError::GettingBundleName)? .ok_or(LoadingError::GettingBundleName)?
.to_str() .to_str()
.ok_or(LoadingError::ConvertingBundleName)? .ok_or(LoadingError::ConvertingBundleName)?
.to_string(); .to_string();
let locale = metadata.locale() let locale = metadata.locale().ok_or(LoadingError::GettingLocale)?;
.ok_or(LoadingError::GettingLocale)?;
let globals_path = &bundle_path let globals_path = &bundle_path
.join(&locale) .join(&locale)
@ -57,6 +52,10 @@ impl CoreBundle {
let globals = globals::LocalizedGlobalsVecs::load(globals_path)?; let globals = globals::LocalizedGlobalsVecs::load(globals_path)?;
Ok(CoreBundle {name, metadata, globals}) Ok(CoreBundle {
name,
metadata,
globals,
})
} }
} }

View file

@ -25,6 +25,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[rustfmt::skip]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedCardRarity>(r#" serde_json::de::from_str::<'static, LocalizedCardRarity>(r#"

View file

@ -1,7 +1,7 @@
//! Module defining structs representing localized card regions. //! Module defining structs representing localized card regions.
use std::collections::HashMap;
use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::region::CardRegion;
use std::collections::HashMap;
/// A Legends of Runeterra [CardRegion], and its associated localization. /// A Legends of Runeterra [CardRegion], and its associated localization.
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
@ -28,7 +28,6 @@ pub type LocalizedCardRegionVec = Vec<LocalizedCardRegion>;
/// An index of [LocalizedCardRegion]s, with [LocalizedCardRegion::region]s as keys. /// An index of [LocalizedCardRegion]s, with [LocalizedCardRegion::region]s as keys.
pub type LocalizedCardRegionIndex = HashMap<CardRegion, LocalizedCardRegion>; pub type LocalizedCardRegionIndex = HashMap<CardRegion, LocalizedCardRegion>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -36,19 +35,22 @@ mod tests {
#[test] #[test]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedCardRegion>(r#" serde_json::de::from_str::<'static, LocalizedCardRegion>(
r#"
{ {
"abbreviation": "NX", "abbreviation": "NX",
"iconAbsolutePath": "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png", "iconAbsolutePath": "http://dd.b.pvp.net/3_11_0/core/en_us/img/regions/icon-noxus.png",
"name": "Noxus", "name": "Noxus",
"nameRef": "Noxus" "nameRef": "Noxus"
} }
"#).unwrap(), "#
)
.unwrap(),
LocalizedCardRegion { LocalizedCardRegion {
region: CardRegion::Noxus, region: CardRegion::Noxus,
name: "Noxus".to_string(), name: "Noxus".to_string(),
abbreviation: "NX".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()
} }
); );
} }

View file

@ -29,6 +29,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[rustfmt::skip]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedCardSet>(r#" serde_json::de::from_str::<'static, LocalizedCardSet>(r#"

View file

@ -1,7 +1,7 @@
//! Module defining structs representing localized spell speeds. //! Module defining structs representing localized spell speeds.
use std::collections::HashMap;
use crate::data::setbundle::speed::SpellSpeed; use crate::data::setbundle::speed::SpellSpeed;
use std::collections::HashMap;
/// A Legends of Runeterra [SpellSpeed], and its associated localization. /// A Legends of Runeterra [SpellSpeed], and its associated localization.
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
@ -19,7 +19,6 @@ pub type LocalizedSpellSpeedVec = Vec<LocalizedSpellSpeed>;
/// An index of [LocalizedSpellSpeed]s, with [LocalizedSpellSpeed::spell_speed]s as keys. /// An index of [LocalizedSpellSpeed]s, with [LocalizedSpellSpeed::spell_speed]s as keys.
pub type LocalizedSpellSpeedIndex = HashMap<SpellSpeed, LocalizedSpellSpeed>; pub type LocalizedSpellSpeedIndex = HashMap<SpellSpeed, LocalizedSpellSpeed>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -27,15 +26,18 @@ mod tests {
#[test] #[test]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedSpellSpeed>(r#" serde_json::de::from_str::<'static, LocalizedSpellSpeed>(
r#"
{ {
"name": "Slow", "name": "Slow",
"nameRef": "Slow" "nameRef": "Slow"
} }
"#).unwrap(), "#
)
.unwrap(),
LocalizedSpellSpeed { LocalizedSpellSpeed {
spell_speed: SpellSpeed::Slow, spell_speed: SpellSpeed::Slow,
name: "Slow".to_string(), name: "Slow".to_string()
} }
); );
} }

View file

@ -30,6 +30,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[rustfmt::skip]
fn deserialize() { fn deserialize() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, LocalizedVocabTerm>(r#" serde_json::de::from_str::<'static, LocalizedVocabTerm>(r#"

View file

@ -2,6 +2,6 @@
//! //!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
pub mod anybundle;
pub mod corebundle; pub mod corebundle;
pub mod setbundle; pub mod setbundle;
pub mod anybundle;

View file

@ -1,6 +1,5 @@
//! Module defining [CardArt]. //! Module defining [CardArt].
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
@ -30,7 +29,6 @@ pub struct CardArt {
pub full_png: String, pub full_png: String,
} }
impl CardArt { impl CardArt {
/// URL to the `.jpg` image of the `en_us` locale of the rendered card, via `poro.steffo.eu`. /// 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! { lazy_static! {
static ref GET_JPG: Regex = Regex::new( static ref GET_JPG: Regex = Regex::new(
r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]png$"# r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]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`. /// 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! { lazy_static! {
static ref GET_JPG: Regex = Regex::new( static ref GET_JPG: Regex = Regex::new(
r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]png$"# r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]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)] #[cfg(test)]
mod tests { mod tests {
use super::CardArt; use super::CardArt;
#[test] #[test]
fn deserialize() { fn deserialize() {
assert_eq!( 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") });
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] #[test]
fn png_to_jpg() { fn png_to_jpg() {
let art = CardArt { let art = CardArt {
card_png: String::from("https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png"), 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!(
assert_eq!(art.full_jpg(), "https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001-full.jpg"); 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"
);
} }
} }

View file

@ -169,6 +169,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[rustfmt::skip]
fn deserialize_card() { fn deserialize_card() {
assert_eq!( assert_eq!(
serde_json::de::from_str::<'static, Card>(r#" serde_json::de::from_str::<'static, Card>(r#"

View file

@ -342,12 +342,14 @@ impl CardKeyword {
/// Returns [Option::None] if no matching [LocalizedCardKeyword] was found, for example for [CardKeyword::Unsupported] keywords. /// Returns [Option::None] if no matching [LocalizedCardKeyword] was found, for example for [CardKeyword::Unsupported] keywords.
/// ///
/// Equivalent to calling [LocalizedCardKeywordIndex::get]. /// 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) hm.get(self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::CardKeyword; use super::CardKeyword;
@ -356,79 +358,202 @@ mod tests {
( $id:ident, $src:literal, $res:expr ) => { ( $id:ident, $src:literal, $res:expr ) => {
#[test] #[test]
fn $id() { 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_burst, r#""Burst""#, CardKeyword::Burst);
test_deserialization!(deserialize_countdown, r#""Countdown""#, CardKeyword::Countdown); test_deserialization!(
test_deserialization!(deserialize_onplay, r#""PlaySkillMark""#, CardKeyword::OnPlay); deserialize_countdown,
test_deserialization!(deserialize_landmark, r#""LandmarkVisualOnly""#, CardKeyword::Landmark); 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_shurima, r#""Shurima""#, CardKeyword::Shurima);
test_deserialization!(deserialize_attach, r#""Attach""#, CardKeyword::Attach); test_deserialization!(deserialize_attach, r#""Attach""#, CardKeyword::Attach);
test_deserialization!(deserialize_noxus, r#""Noxus""#, CardKeyword::Noxus); test_deserialization!(deserialize_noxus, r#""Noxus""#, CardKeyword::Noxus);
test_deserialization!(deserialize_fleeting, r#""Fleeting""#, CardKeyword::Fleeting); 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_nab, r#""Nab""#, CardKeyword::Nab);
test_deserialization!(deserialize_focus, r#""Focus""#, CardKeyword::Focus); 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_invoke, r#""Invoke""#, CardKeyword::Invoke);
test_deserialization!(deserialize_boon, r#""Boon""#, CardKeyword::Boon); test_deserialization!(deserialize_boon, r#""Boon""#, CardKeyword::Boon);
test_deserialization!(deserialize_trap, r#""Autoplay""#, CardKeyword::Trap); test_deserialization!(deserialize_trap, r#""Autoplay""#, CardKeyword::Trap);
test_deserialization!(deserialize_drain, r#""Drain""#, CardKeyword::Drain); 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_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_fast, r#""Fast""#, CardKeyword::Fast);
test_deserialization!(deserialize_bilgewater, r#""Bilgewater""#, CardKeyword::Bilgewater); test_deserialization!(
test_deserialization!(deserialize_runeterra, r#""Runeterra""#, CardKeyword::Runeterra); 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_recall, r#""Recall""#, CardKeyword::Recall);
test_deserialization!(deserialize_weakest, r#""Weakest""#, CardKeyword::Weakest); test_deserialization!(deserialize_weakest, r#""Weakest""#, CardKeyword::Weakest);
test_deserialization!(deserialize_support, r#""Support""#, CardKeyword::Support); test_deserialization!(deserialize_support, r#""Support""#, CardKeyword::Support);
test_deserialization!(deserialize_slow, r#""Slow""#, CardKeyword::Slow); 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_imbue, r#""Imbue""#, CardKeyword::Imbue);
test_deserialization!(deserialize_targon, r#""MtTargon""#, CardKeyword::Targon); test_deserialization!(deserialize_targon, r#""MtTargon""#, CardKeyword::Targon);
test_deserialization!(deserialize_shadowisles, r#""ShadowIsles""#, CardKeyword::ShadowIsles); test_deserialization!(
test_deserialization!(deserialize_auravisualfakekeyword, r#""AuraVisualFakeKeyword""#, CardKeyword::AuraVisualFakeKeyword); 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_ionia, r#""Ionia""#, CardKeyword::Ionia);
test_deserialization!(deserialize_nightfall, r#""Nightfall""#, CardKeyword::Nightfall); test_deserialization!(
test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardKeyword::PiltoverZaun); 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_attune, r#""Attune""#, CardKeyword::Attune);
test_deserialization!(deserialize_daybreak, r#""Daybreak""#, CardKeyword::Daybreak); 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_skill, r#""Skill""#, CardKeyword::Skill);
test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder); test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder);
test_deserialization!(deserialize_doubleattack, r#""DoubleAttack""#, CardKeyword::DoubleAttack); test_deserialization!(
test_deserialization!(deserialize_vulnerable, r#""Vulnerable""#, CardKeyword::Vulnerable); 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_elusive, r#""Elusive""#, CardKeyword::Elusive);
test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun); test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun);
test_deserialization!(deserialize_fated, r#""Fated""#, CardKeyword::Fated); 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_fury, r#""Fury""#, CardKeyword::Fury);
test_deserialization!(deserialize_barrier, r#""Barrier""#, CardKeyword::Barrier); test_deserialization!(deserialize_barrier, r#""Barrier""#, CardKeyword::Barrier);
test_deserialization!(deserialize_immobile, r#""Immobile""#, CardKeyword::Immobile); test_deserialization!(deserialize_immobile, r#""Immobile""#, CardKeyword::Immobile);
test_deserialization!(deserialize_hallowed, r#""Hallowed""#, CardKeyword::Hallowed); test_deserialization!(deserialize_hallowed, r#""Hallowed""#, CardKeyword::Hallowed);
test_deserialization!(deserialize_evolve, r#""Evolve""#, CardKeyword::Evolve); test_deserialization!(deserialize_evolve, r#""Evolve""#, CardKeyword::Evolve);
test_deserialization!(deserialize_frostbite, r#""Frostbite""#, CardKeyword::Frostbite); test_deserialization!(
test_deserialization!(deserialize_overwhelm, r#""Overwhelm""#, CardKeyword::Overwhelm); deserialize_frostbite,
test_deserialization!(deserialize_quickattack, r#""QuickStrike""#, CardKeyword::QuickAttack); 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_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_silenced, r#""Silenced""#, CardKeyword::Silenced);
test_deserialization!(deserialize_spellshield, r#""SpellShield""#, CardKeyword::SpellShield); test_deserialization!(
test_deserialization!(deserialize_lifesteal, r#""Lifesteal""#, CardKeyword::Lifesteal); 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_augment, r#""Augment""#, CardKeyword::Augment);
test_deserialization!(deserialize_impact, r#""Impact""#, CardKeyword::Impact); test_deserialization!(deserialize_impact, r#""Impact""#, CardKeyword::Impact);
test_deserialization!(deserialize_scout, r#""Scout""#, CardKeyword::Scout); 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_lurk, r#""Lurker""#, CardKeyword::Lurk);
test_deserialization!(deserialize_formidable, r#""Formidable""#, CardKeyword::Formidable); test_deserialization!(
test_deserialization!(deserialize_challenger, r#""Challenger""#, CardKeyword::Challenger); 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_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_deep, r#""Deep""#, CardKeyword::Deep);
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardKeyword::Unsupported); test_deserialization!(
deserialize_unsupported,
r#""Xyzzy""#,
CardKeyword::Unsupported
);
} }

View file

@ -3,22 +3,21 @@
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon //! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
//! [Set Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_set-bundles //! [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 super::anybundle::metadata::BundleMetadata;
use crate::data::anybundle::outcomes::{LoadingError, LoadingResult}; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
use std::fs::File;
use std::path::Path;
pub mod card;
pub mod art; pub mod art;
pub mod r#type; pub mod card;
pub mod keyword;
pub mod rarity; pub mod rarity;
pub mod region; pub mod region;
pub mod set; pub mod set;
pub mod speed; pub mod speed;
pub mod keyword;
pub mod subtype; pub mod subtype;
pub mod supertype; pub mod supertype;
pub mod r#type;
/// A parsed [Data Dragon] [Set Bundle]. /// A parsed [Data Dragon] [Set Bundle].
/// ///
@ -35,41 +34,38 @@ pub struct SetBundle {
pub name: String, pub name: String,
} }
impl SetBundle { impl SetBundle {
/// Load a Set Bundle directory to create a [SetBundle] instance. /// Load a Set Bundle directory to create a [SetBundle] instance.
pub fn load(bundle_path: &Path) -> LoadingResult<Self> { pub fn load(bundle_path: &Path) -> LoadingResult<Self> {
let metadata = BundleMetadata::load( let metadata = BundleMetadata::load(&bundle_path.join("metadata.json"))?;
&bundle_path
.join("metadata.json")
)?;
let locale = metadata.locale() let locale = metadata.locale().ok_or(LoadingError::GettingLocale)?;
.ok_or(LoadingError::GettingLocale)?;
let name = bundle_path.file_name() let name = bundle_path
.file_name()
.ok_or(LoadingError::GettingBundleName)?; .ok_or(LoadingError::GettingBundleName)?;
let data_path = { let data_path = {
let mut json_filename = name.to_os_string(); let mut json_filename = name.to_os_string();
json_filename.push(".json"); json_filename.push(".json");
&bundle_path &bundle_path.join(&locale).join("data").join(&json_filename)
.join(&locale)
.join("data")
.join(&json_filename)
}; };
let name = name.to_str() let name = name
.to_str()
.ok_or(LoadingError::ConvertingBundleName)? .ok_or(LoadingError::ConvertingBundleName)?
.to_string(); .to_string();
let cards = File::open(data_path) let cards = File::open(data_path).map_err(LoadingError::OpeningFile)?;
.map_err(LoadingError::OpeningFile)?;
let cards = serde_json::de::from_reader::<File, Vec<card::Card>>(cards) let cards = serde_json::de::from_reader::<File, Vec<card::Card>>(cards)
.map_err(LoadingError::Deserializing)?; .map_err(LoadingError::Deserializing)?;
Ok(SetBundle {metadata, cards, name}) Ok(SetBundle {
metadata,
cards,
name,
})
} }
} }

View file

@ -27,12 +27,14 @@ impl CardRarity {
/// Returns [Option::None] if no matching [LocalizedCardRarity] was found, for example rarities missing from the index. /// Returns [Option::None] if no matching [LocalizedCardRarity] was found, for example rarities missing from the index.
/// ///
/// Equivalent to calling [LocalizedCardRarityIndex::get]. /// 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) hm.get(self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::CardRarity; use super::CardRarity;
@ -41,9 +43,12 @@ mod tests {
( $id:ident, $src:literal, $res:expr ) => { ( $id:ident, $src:literal, $res:expr ) => {
#[test] #[test]
fn $id() { 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); test_deserialization!(deserialize_none, r#""None""#, CardRarity::None);

View file

@ -55,6 +55,7 @@ impl CardRegion {
#[cfg(test)] #[cfg(test)]
#[rustfmt::skip]
mod tests { mod tests {
use super::CardRegion; use super::CardRegion;

View file

@ -52,7 +52,6 @@ impl CardSet {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::CardSet; use super::CardSet;
@ -61,9 +60,12 @@ mod tests {
( $id:ident, $src:literal, $res:expr ) => { ( $id:ident, $src:literal, $res:expr ) => {
#[test] #[test]
fn $id() { 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); test_deserialization!(deserialize_set1, r#""Set1""#, CardSet::Foundations);

View file

@ -22,12 +22,14 @@ impl SpellSpeed {
/// Returns [Option::None] if no matching [LocalizedSpellSpeed] was found, for example spell speeds missing from the index. /// Returns [Option::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) -> Option<&'hm LocalizedSpellSpeed> { pub fn localized<'hm>(
&self,
hm: &'hm LocalizedSpellSpeedIndex,
) -> Option<&'hm LocalizedSpellSpeed> {
hm.get(self) hm.get(self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::SpellSpeed; use super::SpellSpeed;
@ -36,9 +38,12 @@ mod tests {
( $id:ident, $src:literal, $res:expr ) => { ( $id:ident, $src:literal, $res:expr ) => {
#[test] #[test]
fn $id() { 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); test_deserialization!(deserialize_none, r#""""#, SpellSpeed::None);

View file

@ -1,6 +1,5 @@
//! Module defining [CardType]. //! Module defining [CardType].
/// A possible [Card](super::card::Card) type. /// 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). /// 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, Unsupported,
} }
impl From<&CardType> for String { impl From<&CardType> for String {
fn from(r#type: &CardType) -> Self { fn from(r#type: &CardType) -> Self {
match r#type { match r#type {
@ -45,7 +43,6 @@ impl From<&CardType> for String {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::CardType; use super::CardType;
@ -54,9 +51,12 @@ mod tests {
( $id:ident, $src:literal, $res:expr ) => { ( $id:ident, $src:literal, $res:expr ) => {
#[test] #[test]
fn $id() { 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); 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_ability, r#""Ability""#, CardType::Ability);
test_deserialization!(deserialize_landmark, r#""Landmark""#, CardType::Landmark); test_deserialization!(deserialize_landmark, r#""Landmark""#, CardType::Landmark);
test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardType::Unsupported); test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardType::Unsupported);
} }

View file

@ -1,4 +1,3 @@
//! Module providing utilities to be used in the `patched_porobot_matrix` executable target. //! 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. //! 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.

View file

@ -1,14 +1,13 @@
//! Module defining a search engine to find [Card]s. //! 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::collector::TopDocs;
use tantivy::query::{QueryParser, QueryParserError}; use tantivy::query::{QueryParser, QueryParserError};
use tantivy::schema::{Field, NumericOptions, Schema, TextOptions}; use tantivy::schema::{Field, NumericOptions, Schema, TextOptions};
use tantivy::tokenizer::TextAnalyzer; use tantivy::tokenizer::TextAnalyzer;
use itertools::Itertools; use tantivy::{Document, Index, IndexReader, IndexWriter};
use crate::data::corebundle::globals::LocalizedGlobalsIndexes;
use crate::data::setbundle::card::{Card, CardIndex};
/// The search engine. /// The search engine.
/// ///
@ -29,10 +28,9 @@ pub struct CardSearchEngine {
pub globals: LocalizedGlobalsIndexes, pub globals: LocalizedGlobalsIndexes,
/// Cards searchable in the search engine. /// Cards searchable in the search engine.
pub cards: CardIndex pub cards: CardIndex,
} }
impl CardSearchEngine { impl CardSearchEngine {
/// Create the [tantivy::tokenizer::TextAnalyzer] for card text. /// Create the [tantivy::tokenizer::TextAnalyzer] for card text.
/// ///
@ -40,8 +38,7 @@ impl CardSearchEngine {
fn tokenizer() -> TextAnalyzer { fn tokenizer() -> TextAnalyzer {
use tantivy::tokenizer::*; use tantivy::tokenizer::*;
TextAnalyzer::from(SimpleTokenizer) TextAnalyzer::from(SimpleTokenizer).filter(LowerCaser)
.filter(LowerCaser)
} }
/// Create the [tantivy::schema::TextOptions] for card codes. /// Create the [tantivy::schema::TextOptions] for card codes.
@ -54,9 +51,10 @@ impl CardSearchEngine {
use tantivy::schema::*; use tantivy::schema::*;
TextOptions::default() TextOptions::default()
.set_indexing_options(TextFieldIndexing::default() .set_indexing_options(
.set_tokenizer("card") TextFieldIndexing::default()
.set_index_option(IndexRecordOption::Basic) .set_tokenizer("card")
.set_index_option(IndexRecordOption::Basic),
) )
.set_stored() .set_stored()
.set_fast() .set_fast()
@ -70,11 +68,11 @@ impl CardSearchEngine {
fn options_keyword() -> TextOptions { fn options_keyword() -> TextOptions {
use tantivy::schema::*; use tantivy::schema::*;
TextOptions::default() TextOptions::default().set_indexing_options(
.set_indexing_options(TextFieldIndexing::default() TextFieldIndexing::default()
.set_tokenizer("card") .set_tokenizer("card")
.set_index_option(IndexRecordOption::Basic) .set_index_option(IndexRecordOption::Basic),
) )
} }
/// Create the [tantivy::schema::TextOptions] for card text fields. /// Create the [tantivy::schema::TextOptions] for card text fields.
@ -85,11 +83,11 @@ impl CardSearchEngine {
fn options_text() -> TextOptions { fn options_text() -> TextOptions {
use tantivy::schema::*; use tantivy::schema::*;
TextOptions::default() TextOptions::default().set_indexing_options(
.set_indexing_options(TextFieldIndexing::default() TextFieldIndexing::default()
.set_tokenizer("card") .set_tokenizer("card")
.set_index_option(IndexRecordOption::WithFreqsAndPositions) .set_index_option(IndexRecordOption::WithFreqsAndPositions),
) )
} }
/// Create the [tantivy::schema::NumericOptions] for card numeric fields. /// Create the [tantivy::schema::NumericOptions] for card numeric fields.
@ -99,8 +97,7 @@ impl CardSearchEngine {
fn options_number() -> NumericOptions { fn options_number() -> NumericOptions {
use tantivy::schema::*; use tantivy::schema::*;
NumericOptions::default() NumericOptions::default().set_indexed()
.set_indexed()
} }
/// Create the [Schema] for the search engine. /// Create the [Schema] for the search engine.
@ -163,32 +160,66 @@ impl CardSearchEngine {
/// Create a [CardSchemaFields] object from the given schema. /// Create a [CardSchemaFields] object from the given schema.
fn schema_fields(schema: &Schema) -> CardSchemaFields { fn schema_fields(schema: &Schema) -> CardSchemaFields {
CardSchemaFields { CardSchemaFields {
code: schema.get_field("code").expect("schema to have a 'code' field"), code: schema
name: schema.get_field("name").expect("schema to have a 'name' field"), .get_field("code")
r#type: schema.get_field("type").expect("schema to have a 'type' field"), .expect("schema to have a 'code' field"),
set: schema.get_field("set").expect("schema to have a 'set' field"), name: schema
rarity: schema.get_field("rarity").expect("schema to have a 'rarity' field"), .get_field("name")
collectible: schema.get_field("collectible").expect("schema to have a 'collectible' field"), .expect("schema to have a 'name' field"),
regions: schema.get_field("regions").expect("schema to have a 'regions' field"), r#type: schema
attack: schema.get_field("attack").expect("schema to have a 'attack' field"), .get_field("type")
cost: schema.get_field("cost").expect("schema to have a 'cost' field"), .expect("schema to have a 'type' field"),
health: schema.get_field("health").expect("schema to have a 'health' field"), set: schema
spellspeed: schema.get_field("spellspeed").expect("schema to have a 'spellspeed' field"), .get_field("set")
keywords: schema.get_field("keywords").expect("schema to have a 'keywords' field"), .expect("schema to have a 'set' field"),
description: schema.get_field("description").expect("schema to have a 'description' field"), rarity: schema
levelup: schema.get_field("levelup").expect("schema to have a 'levelup' field"), .get_field("rarity")
flavor: schema.get_field("flavor").expect("schema to have a 'flavor' field"), .expect("schema to have a 'rarity' field"),
artist: schema.get_field("artist").expect("schema to have a 'artist' field"), collectible: schema
subtypes: schema.get_field("subtypes").expect("schema to have a 'subtypes' field"), .get_field("collectible")
supertype: schema.get_field("supertype").expect("schema to have a 'supertype' field"), .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. /// Build [in RAM](Index::create_in_ram) the [Index] of the search engine.
fn index() -> Index { fn index() -> Index {
Index::create_in_ram( Index::create_in_ram(Self::schema())
Self::schema()
)
} }
/// Build a [IndexWriter] with the optimal configuration for the search engine. /// Build a [IndexWriter] with the optimal configuration for the search engine.
@ -210,7 +241,11 @@ impl CardSearchEngine {
} }
/// Create a [Document] from a [Card]. /// 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; use tantivy::doc;
doc!( doc!(
@ -268,7 +303,7 @@ impl CardSearchEngine {
fields.artist, fields.artist,
fields.subtypes, fields.subtypes,
fields.supertype, fields.supertype,
] ],
); );
parser.set_conjunction_by_default(); parser.set_conjunction_by_default();
parser.set_field_boost(fields.code, 3.0); parser.set_field_boost(fields.code, 3.0);
@ -288,16 +323,24 @@ impl CardSearchEngine {
let mut writer = Self::writer(&index); let mut writer = Self::writer(&index);
for card in cards.values() { for card in cards.values() {
let document = Self::document(&fields, &globals, card.clone()); 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"); .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"); .expect("IndexWriter threads to not panic or die before commit");
let parser = Self::parser(&index, fields); let parser = Self::parser(&index, fields);
let reader = Self::reader(&index); let reader = Self::reader(&index);
Self {index, reader, parser, globals, cards} Self {
index,
reader,
parser,
globals,
cards,
}
} }
/// Perform a query on the search engine. /// Perform a query on the search engine.
@ -306,13 +349,18 @@ impl CardSearchEngine {
let query = self.parser.parse_query(input)?; 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"); .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"); .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(|(_score, address)| searcher.doc(address.to_owned()).ok())
.filter_map(|doc| doc.get_first(f_code).cloned()) .filter_map(|doc| doc.get_first(f_code).cloned())
.filter_map(|field| field.as_text().map(String::from)) .filter_map(|field| field.as_text().map(String::from))
@ -323,7 +371,6 @@ impl CardSearchEngine {
} }
} }
/// Struct containing all retrieved [CardSearchEngine] [Field]s. /// 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. /// This makes it easier to pass them around without having to re-fetch them every time they are used.

View file

@ -2,45 +2,35 @@
//! //!
//! [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style //! [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::globals::LocalizedGlobalsIndexes;
use crate::data::corebundle::keyword::LocalizedCardKeywordIndex; use crate::data::corebundle::keyword::LocalizedCardKeywordIndex;
use crate::data::corebundle::region::LocalizedCardRegionIndex; use crate::data::corebundle::region::LocalizedCardRegionIndex;
use crate::data::corebundle::set::LocalizedCardSetIndex; use crate::data::corebundle::set::LocalizedCardSetIndex;
use crate::data::setbundle::card::Card;
use crate::data::setbundle::keyword::CardKeyword; use crate::data::setbundle::keyword::CardKeyword;
use crate::data::setbundle::r#type::CardType;
use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::region::CardRegion;
use crate::data::setbundle::set::CardSet; use crate::data::setbundle::set::CardSet;
use crate::data::setbundle::subtype::CardSubtype; use crate::data::setbundle::subtype::CardSubtype;
use crate::data::setbundle::supertype::CardSupertype; use crate::data::setbundle::supertype::CardSupertype;
use itertools::Itertools;
use teloxide::utils::html::escape;
/// Render a [Card] in [Telegram Bot HTML]. /// Render a [Card] in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String { pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
let title = format!( let title = format!("<b><u>{}</u></b>\n", escape(&card.name),);
"<b><u>{}</u></b>\n",
escape(&card.name),
);
let stats = match &card.r#type { let stats = match &card.r#type {
CardType::Spell => format!( CardType::Spell => format!("{} mana\n\n", escape(&card.cost.to_string()),),
"{} mana\n\n",
escape(&card.cost.to_string()),
),
CardType::Unit => format!( CardType::Unit => format!(
"{} mana {}|{}\n\n", "{} mana {}|{}\n\n",
escape(&card.cost.to_string()), escape(&card.cost.to_string()),
escape(&card.attack.to_string()), escape(&card.attack.to_string()),
escape(&card.health.to_string()), escape(&card.health.to_string()),
), ),
CardType::Landmark => format!( CardType::Landmark => format!("{} mana\n\n", &card.cost),
"{} mana\n\n",
&card.cost
),
_ => "".to_string(), _ => "".to_string(),
}; };
@ -55,60 +45,50 @@ pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
let description = display_description(&card.localized_description_text); let description = display_description(&card.localized_description_text);
let levelup = display_levelup(&card.localized_levelup_text); let levelup = display_levelup(&card.localized_levelup_text);
let flavor = format!( let flavor = format!("<i>{}</i>\n", escape(&card.localized_flavor_text));
"<i>{}</i>\n",
escape(&card.localized_flavor_text)
);
let artist = format!( let artist = format!(
r#"<a href="{}">Illustration</a> by {}"#, r#"<a href="{}">Illustration</a> 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) escape(&card.artist_name)
); );
format!( format!(
"{}{}{}{}{}{}-----\n{}{}", "{}{}{}{}{}{}-----\n{}{}",
&title, &title, &breadcrumbs, &keywords, &stats, &description, &levelup, &flavor, &artist,
&breadcrumbs,
&keywords,
&stats,
&description,
&levelup,
&flavor,
&artist,
) )
} }
/// Render a [CardSet] in [Telegram Bot HTML]. /// Render a [CardSet] in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String { fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String {
format!( format!(
"<i>{}</i>", "<i>{}</i>",
set set.localized(hm)
.localized(hm)
.map(|o| format!("<i>{}</i>", escape(&o.name))) .map(|o| format!("<i>{}</i>", escape(&o.name)))
.unwrap_or_else(|| "Unknown".to_string()) .unwrap_or_else(|| "Unknown".to_string())
) )
} }
/// Render a slice of [CardRegion]s in [Telegram Bot HTML]. /// Render a slice of [CardRegion]s in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_regions(regions: &[CardRegion], hm: &LocalizedCardRegionIndex) -> String { fn display_regions(regions: &[CardRegion], hm: &LocalizedCardRegionIndex) -> String {
regions regions
.iter() .iter()
.map(|region| region .map(|region| {
.localized(hm) region
.map(|o| format!("<i>{}</i>", escape(&o.name))) .localized(hm)
.unwrap_or_else(|| "Unknown".to_string()) .map(|o| format!("<i>{}</i>", escape(&o.name)))
) .unwrap_or_else(|| "Unknown".to_string())
})
.join(", ") .join(", ")
} }
/// Render the [CardType], the [CardSupertype] and the [CardSubtype]s in [Telegram Bot HTML]. /// Render the [CardType], the [CardSupertype] and the [CardSubtype]s in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [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(); let mut result = String::new();
if supertype != "" { if supertype != "" {
result.push_str(&*format!( result.push_str(&*format!("<i>{}</i> ", escape(&supertype),));
"<i>{}</i> ",
escape(&supertype),
));
}; };
result.push_str(&*format!( result.push_str(&*format!("<i>{}</i>", escape(&*String::from(r#type)),));
"<i>{}</i>",
escape(&*String::from(r#type)),
));
if subtypes.len() > 0 { if subtypes.len() > 0 {
result.push_str( result.push_str(&*format!(
&*format!( " {}",
" {}", subtypes
subtypes.iter() .iter()
.map(|subtype| format!("<i>{}</i>", escape(&subtype))) .map(|subtype| format!("<i>{}</i>", escape(&subtype)))
.join(", ") .join(", ")
) ))
)
} }
result result
} }
/// Render a slice of [CardKeyword]s in [Telegram Bot HTML]. /// Render a slice of [CardKeyword]s in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [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 .map(|keyword| keyword
.localized(hm) .localized(hm)
.map(|o| format!("[<b>{}</b>]", escape(&o.name))) .map(|o| format!("[<b>{}</b>]", escape(&o.name)))
.unwrap_or_else(|| "Unknown".to_string()) .unwrap_or_else(|| "Unknown".to_string()))
)
.join(" ") .join(" ")
) )
} }
/// Render a [Card::localized_description_text] in [Telegram Bot HTML]. /// Render a [Card::localized_description_text] in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_description(description: &String) -> String { fn display_description(description: &String) -> String {
if description == "" { if description == "" {
"".to_string() "".to_string()
} } else {
else {
format!("{}\n\n", escape(&description)) format!("{}\n\n", escape(&description))
} }
} }
/// Render a [Card::localized_levelup_text] in [Telegram Bot HTML]. /// Render a [Card::localized_levelup_text] in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_levelup(levelup: &String) -> String { fn display_levelup(levelup: &String) -> String {
if levelup == "" { if levelup == "" {
"".to_string() "".to_string()
} } else {
else {
format!("<u>Level up</u>: {}\n\n", escape(&levelup)) format!("<u>Level up</u>: {}\n\n", escape(&levelup))
} }
} }

View file

@ -1,18 +1,19 @@
//! Module providing handlers for @patchedporobot on Telegram. //! Module providing handlers for @patchedporobot on Telegram.
use crate::search::cardsearch::CardSearchEngine;
use crate::telegram::inline::card_to_inlinequeryresult;
use itertools::Itertools; use itertools::Itertools;
use log::*; use log::*;
use teloxide::dispatching::DpHandlerDescription; use teloxide::dispatching::DpHandlerDescription;
use teloxide::payloads::{AnswerInlineQuery, SendMessage}; use teloxide::payloads::{AnswerInlineQuery, SendMessage};
use teloxide::requests::{JsonRequest, ResponseResult};
use teloxide::prelude::*; use teloxide::prelude::*;
use teloxide::requests::{JsonRequest, ResponseResult};
use teloxide::types::{ParseMode, Recipient}; 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]. /// 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| { Update::filter_inline_query().chain(dptree::endpoint(move |query: InlineQuery, bot: Bot| {
info!("Handling inline query: `{}`", &query.query); info!("Handling inline query: `{}`", &query.query);
@ -27,7 +28,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend
next_offset: None, next_offset: None,
switch_pm_text: Some("How to search cards".to_string()), switch_pm_text: Some("How to search cards".to_string()),
switch_pm_parameter: Some("err-no-query".to_string()), switch_pm_parameter: Some("err-no-query".to_string()),
} };
} }
debug!("Querying the search engine..."); debug!("Querying the search engine...");
@ -43,7 +44,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend
next_offset: None, next_offset: None,
switch_pm_text: Some("Invalid query syntax".to_string()), switch_pm_text: Some("Invalid query syntax".to_string()),
switch_pm_parameter: Some("err-invalid-query".to_string()), switch_pm_parameter: Some("err-invalid-query".to_string()),
} };
} }
let results = results.unwrap(); let results = results.unwrap();
@ -58,7 +59,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend
next_offset: None, next_offset: None,
switch_pm_text: Some("No cards found".to_string()), switch_pm_text: Some("No cards found".to_string()),
switch_pm_parameter: Some("err-no-results".to_string()), switch_pm_parameter: Some("err-no-results".to_string()),
} };
} }
debug!("Found {} cards.", &len); debug!("Found {} cards.", &len);
@ -73,7 +74,7 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend
next_offset: None, next_offset: None,
switch_pm_text: None, switch_pm_text: None,
switch_pm_parameter: None, switch_pm_parameter: None,
} };
}; };
async move { async move {
@ -88,7 +89,6 @@ pub fn inline_query_handler(engine: CardSearchEngine) -> Handler<'static, Depend
})) }))
} }
const WELCOME_MESSAGE: &'static str = r#" 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! 👋 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!
<i>@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.</i> <i>@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.</i>
"#; "#;
/// Handle all messages by replying with the help text. /// 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| { Update::filter_message().chain(dptree::endpoint(move |message: Message, bot: Bot| {
info!("Handling private message: `{:?}`", &message.text()); info!("Handling private message: `{:?}`", &message.text());
@ -123,7 +123,7 @@ pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>,
protect_content: None, protect_content: None,
reply_to_message_id: None, reply_to_message_id: None,
allow_sending_without_reply: None, allow_sending_without_reply: None,
reply_markup: None reply_markup: None,
}; };
async move { async move {
@ -136,4 +136,4 @@ pub fn message_handler() -> Handler<'static, DependencyMap, ResponseResult<()>,
respond(()) respond(())
} }
})) }))
} }

View file

@ -2,14 +2,16 @@
//! //!
//! [inline mode]: https://core.telegram.org/bots/api#inline-mode //! [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::corebundle::globals::LocalizedGlobalsIndexes;
use crate::data::setbundle::card::Card; use crate::data::setbundle::card::Card;
use crate::telegram::display::display_card; use crate::telegram::display::display_card;
use teloxide::types::{InlineQueryResult, InlineQueryResultPhoto, ParseMode};
/// Converts a [Card] into a [InlineQueryResult]. /// 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 { InlineQueryResult::Photo(InlineQueryResultPhoto {
id: card.code.to_owned(), id: card.code.to_owned(),
title: Some(card.name.to_owned()), title: Some(card.name.to_owned()),
@ -18,16 +20,17 @@ pub fn card_to_inlinequeryresult(globals: &LocalizedGlobalsIndexes, card: &Card)
photo_url: card photo_url: card
.main_art() .main_art()
.expect("Card to have at least one illustration") .expect("Card to have at least one illustration")
.card_jpg().parse() .card_jpg()
.parse()
.expect("Card to have a valid card_jpg URL"), .expect("Card to have a valid card_jpg URL"),
thumb_url: card thumb_url: card
.main_art() .main_art()
.expect("Card to have at least one illustration") .expect("Card to have at least one illustration")
.card_jpg().parse() .card_jpg()
.parse()
.expect("Card to have a valid card_jpg URL"), .expect("Card to have a valid card_jpg URL"),
photo_width: Some(680), photo_width: Some(680),
photo_height: Some(1024), photo_height: Some(1024),
description: None, description: None,
caption_entities: None, caption_entities: None,
reply_markup: None, reply_markup: None,

View file

@ -1,13 +1,13 @@
//! This module defines the [`main`] function for [`patched_porobot_telegram`]. //! 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::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::search::cardsearch::CardSearchEngine;
use crate::telegram::handler::{inline_query_handler, message_handler}; use crate::telegram::handler::{inline_query_handler, message_handler};
use log::*;
use std::path::PathBuf;
use teloxide::prelude::*; use teloxide::prelude::*;
/// The main function that [`patched_porobot_telegram`] should run when it's started. /// 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!("Logger initialized successfully!");
debug!("Loading bundles..."); debug!("Loading bundles...");
let core = CoreBundle::load(&*PathBuf::from("./data/core-en_us")).expect("to be able to load `core-en_us` bundle"); let core = CoreBundle::load(&*PathBuf::from("./data/core-en_us"))
let set1 = SetBundle::load(&*PathBuf::from("./data/set1-en_us")).expect("to be able to load `set1-en_us` bundle"); .expect("to be able to load `core-en_us` bundle");
let set2 = SetBundle::load(&*PathBuf::from("./data/set2-en_us")).expect("to be able to load `set2-en_us` bundle"); let set1 = SetBundle::load(&*PathBuf::from("./data/set1-en_us"))
let set3 = SetBundle::load(&*PathBuf::from("./data/set3-en_us")).expect("to be able to load `set3-en_us` bundle"); .expect("to be able to load `set1-en_us` bundle");
let set4 = SetBundle::load(&*PathBuf::from("./data/set4-en_us")).expect("to be able to load `set4-en_us` bundle"); let set2 = SetBundle::load(&*PathBuf::from("./data/set2-en_us"))
let set5 = SetBundle::load(&*PathBuf::from("./data/set5-en_us")).expect("to be able to load `set5-en_us` bundle"); .expect("to be able to load `set2-en_us` bundle");
let set6 = SetBundle::load(&*PathBuf::from("./data/set6-en_us")).expect("to be able to load `set6-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!("Loaded all bundles!");
debug!("Indexing globals..."); debug!("Indexing globals...");
@ -31,13 +38,9 @@ pub async fn main() {
debug!("Indexing cards..."); debug!("Indexing cards...");
let cards: Vec<Card> = [ let cards: Vec<Card> = [
set1.cards, set1.cards, set2.cards, set3.cards, set4.cards, set5.cards, set6.cards,
set2.cards, ]
set3.cards, .concat();
set4.cards,
set5.cards,
set6.cards
].concat();
let mut index = CardIndex::new(); let mut index = CardIndex::new();
for card in cards { for card in cards {
@ -52,7 +55,11 @@ pub async fn main() {
debug!("Creating Telegram bot with parameters from the environment..."); debug!("Creating Telegram bot with parameters from the environment...");
let bot = Bot::from_env(); 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!("Created Telegram bot!");
debug!("Creating handlers..."); debug!("Creating handlers...");
@ -61,10 +68,13 @@ pub async fn main() {
.branch(message_handler()); .branch(message_handler());
debug!("Created handlers!"); 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) Dispatcher::builder(bot, handler)
.enable_ctrlc_handler() .enable_ctrlc_handler()
.build() .build()
.dispatch() .dispatch()
.await; .await;
} }

View file

@ -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. //! 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 display;
pub mod inline;
pub mod handler; pub mod handler;
pub mod inline;
pub mod main; pub mod main;