1
Fork 0
mirror of https://github.com/Steffo99/patched-porobot.git synced 2024-10-16 09:37:27 +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
No known key found for this signature in database
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(hidden)]
#[tokio::main]
async fn main() {

View file

@ -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<String>
pub locales: Vec<String>,
}
impl BundleMetadata {
/// Load a `metadata.json` file to create a [BundleMetadata] instance.
pub fn load(path: &Path) -> LoadingResult<Self> {
let file = File::open(path)
.map_err(LoadingError::OpeningFile)?;
let data = serde_json::de::from_reader::<File, Self>(file)
.map_err(LoadingError::Deserializing)?;
let file = File::open(path).map_err(LoadingError::OpeningFile)?;
let data =
serde_json::de::from_reader::<File, Self>(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()]
}

View file

@ -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 {

View file

@ -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<Self> {
let file = File::open(path)
.map_err(LoadingError::OpeningFile)?;
let data = serde_json::de::from_reader::<File, Self>(file)
.map_err(LoadingError::Deserializing)?;
let file = File::open(path).map_err(LoadingError::OpeningFile)?;
let data =
serde_json::de::from_reader::<File, Self>(file).map_err(LoadingError::Deserializing)?;
Ok(data)
}
}
impl From<LocalizedGlobalsVecs> for LocalizedGlobalsIndexes {
fn from(o: LocalizedGlobalsVecs) -> Self {
Self {
@ -130,7 +127,6 @@ impl From<LocalizedGlobalsVecs> 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() }] }
)
}
}

View file

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

View file

@ -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<Self> {
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,
})
}
}

View file

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

View file

@ -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<LocalizedCardRegion>;
/// An index of [LocalizedCardRegion]s, with [LocalizedCardRegion::region]s as keys.
pub type LocalizedCardRegionIndex = HashMap<CardRegion, LocalizedCardRegion>;
#[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()
}
);
}

View file

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

View file

@ -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<LocalizedSpellSpeed>;
/// An index of [LocalizedSpellSpeed]s, with [LocalizedSpellSpeed::spell_speed]s as keys.
pub type LocalizedSpellSpeedIndex = HashMap<SpellSpeed, LocalizedSpellSpeed>;
#[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()
}
);
}

View file

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

View file

@ -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;

View file

@ -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<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`.
@ -66,37 +70,44 @@ impl CardArt {
lazy_static! {
static ref GET_JPG: Regex = Regex::new(
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)]
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"
);
}
}
}

View file

@ -169,6 +169,7 @@ mod tests {
use super::*;
#[test]
#[rustfmt::skip]
fn deserialize_card() {
assert_eq!(
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.
///
/// 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
);
}

View file

@ -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<Self> {
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::<File, Vec<card::Card>>(cards)
.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.
///
/// 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);

View file

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

View file

@ -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);

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.
///
/// 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);

View file

@ -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);
}
}

View file

@ -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.

View file

@ -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.

View file

@ -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!(
"<b><u>{}</u></b>\n",
escape(&card.name),
);
let title = format!("<b><u>{}</u></b>\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!(
"<i>{}</i>\n",
escape(&card.localized_flavor_text)
);
let flavor = format!("<i>{}</i>\n", escape(&card.localized_flavor_text));
let artist = format!(
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)
);
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!(
"<i>{}</i>",
set
.localized(hm)
set.localized(hm)
.map(|o| format!("<i>{}</i>", 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!("<i>{}</i>", escape(&o.name)))
.unwrap_or_else(|| "Unknown".to_string())
)
.map(|region| {
region
.localized(hm)
.map(|o| format!("<i>{}</i>", 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!(
"<i>{}</i> ",
escape(&supertype),
));
result.push_str(&*format!("<i>{}</i> ", escape(&supertype),));
};
result.push_str(&*format!(
"<i>{}</i>",
escape(&*String::from(r#type)),
));
result.push_str(&*format!("<i>{}</i>", escape(&*String::from(r#type)),));
if subtypes.len() > 0 {
result.push_str(
&*format!(
" {}",
subtypes.iter()
.map(|subtype| format!("<i>{}</i>", escape(&subtype)))
.join(", ")
)
)
result.push_str(&*format!(
" {}",
subtypes
.iter()
.map(|subtype| format!("<i>{}</i>", 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!("[<b>{}</b>]", 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!("<u>Level up</u>: {}\n\n", escape(&levelup))
}
}
}

View file

@ -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!
<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.
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(())
}
}))
}
}

View file

@ -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,

View file

@ -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<Card> = [
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;
}
}

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