1
Fork 0
mirror of https://github.com/Steffo99/patched-porobot.git synced 2024-12-23 01:54:22 +00:00

Perfect data module

This commit is contained in:
Steffo 2022-08-06 19:44:44 +02:00
parent 05a8a5c4a3
commit 0bd71f35d4
Signed by: steffo
GPG key ID: 6965406171929D01
31 changed files with 303 additions and 138 deletions

View file

@ -0,0 +1,3 @@
<component name="ProjectDictionaryState">
<dictionary name="steffo" />
</component>

View file

@ -3,8 +3,8 @@
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/data" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/card-data" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" /> <excludeFolder url="file://$MODULE_DIR$/target" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />

View file

@ -32,9 +32,9 @@ tokio = { version = "1.20.1", features = ["rt-multi-thread", "macros"], optiona
# data = [] # Always included # data = [] # Always included
exec = ["pretty_env_logger", "glob"] exec = ["pretty_env_logger", "glob"]
search = ["tantivy"] search = ["tantivy"]
telegram = ["search", "teloxide", "reqwest", "tokio"] telegram = ["exec", "search", "teloxide", "reqwest", "tokio"]
# discord = ["search"] # discord = ["exec", "search"]
# matrix = ["search"] # matrix = ["exec", "search"]
[lib] [lib]
name = "patched_porobot" name = "patched_porobot"

View file

@ -1,6 +1,10 @@
# ![](icon.jpg) patched-porobot # ![](icon.jpg) patched-porobot
WIP: Legends of Runeterra card bot WIP: Legends of Runeterra card bot
## Legal
patched-porobot 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.
## Discord Emoji Codes ## Discord Emoji Codes

View file

@ -1,12 +1,14 @@
//! This module defines [BundleMetadata], the contents of `metadata.json`. //! Module defining [BundleMetadata], the contents of the `metadata.json` present in all [Data Dragon] Bundles.
//!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use crate::data::outcomes::{LoadingError, LoadingResult}; 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.
/// ///
/// The specification defines more fields, but they are missing from the output files. /// The specification defines more fields, but they are missing from the output files:
/// ///
/// > ```json /// > ```json
/// > { /// > {
@ -27,17 +29,41 @@ pub struct BundleMetadata {
impl BundleMetadata { impl BundleMetadata {
/// Load a `metadata.json` file to create a [LocalizedGlobalsVecs] 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::Loading)?; .map_err(LoadingError::OpeningFile)?;
let data = serde_json::de::from_reader::<File, Self>(file) let data = serde_json::de::from_reader::<File, Self>(file)
.map_err(LoadingError::Parsing)?; .map_err(LoadingError::Deserializing)?;
Ok(data) Ok(data)
} }
/// Get a reference to the first (and probably only) locale defined in BundleMetadata. /// Get a reference to the first (and probably only) locale defined in BundleMetadata.
///
/// Equivalent to calling [BundleMetadata].[locales](BundleMetadata::locales).[get(0)]([T]::get).
pub fn locale(&self) -> Option<&String> { pub fn locale(&self) -> Option<&String> {
self.locales.get(0) self.locales.get(0)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
assert_eq!(
serde_json::de::from_str::<'static, BundleMetadata>(r#"
{
"locales": [
"en_us"
]
}
"#).unwrap(),
BundleMetadata {
locales: vec!["en_us".to_string()]
}
);
}
}

View file

@ -1,3 +1,6 @@
//! This module defines structs common to all Data Dragon Bundles. //! Definitions of structs common to all [Data Dragon] Bundles.
//!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
pub mod metadata; pub mod metadata;
pub mod outcomes;

View file

@ -0,0 +1,22 @@
//! Module defining [Result] and [Result::Err] variants for actions related to [Data Dragon] Bundles.
//!
//! [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 {
/// Could not get the locale from the `metadata.json` file.
GettingLocale,
/// Could not get the bundle name from the operating system.
GettingBundleName,
/// Could not convert the bundle name from a [OsString](std::ffi::OsString) to a [String].
ConvertingBundleName,
/// Could not use [File::open](std::fs::File::open) on a data file.
OpeningFile(std::io::Error),
/// Could not deserialize a data file.
Deserializing(serde_json::Error),
}
/// The result of the loading of a Legends of Runeterra bundle.
pub type LoadingResult<T> = Result<T, LoadingError>;

View file

@ -1,8 +1,8 @@
//! This module defines [LocalizedGlobalsVecs] and [LocalizedGlobalsIndexes], structs representing the data contained in the `globals.json` files. //! Module defining structs representing data contained in `globals.json` files.
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use crate::data::outcomes::{LoadingError, LoadingResult}; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
use super::vocabterm::{LocalizedVocabTermVec, LocalizedVocabTermIndex}; use super::vocabterm::{LocalizedVocabTermVec, LocalizedVocabTermIndex};
use super::keyword::{LocalizedCardKeywordVec, LocalizedCardKeywordIndex}; use super::keyword::{LocalizedCardKeywordVec, LocalizedCardKeywordIndex};
use super::region::{LocalizedCardRegionVec, LocalizedCardRegionIndex}; use super::region::{LocalizedCardRegionVec, LocalizedCardRegionIndex};
@ -10,41 +10,61 @@ use super::speed::{LocalizedSpellSpeedVec, LocalizedSpellSpeedIndex};
use super::rarity::{LocalizedCardRarityVec, LocalizedCardRarityIndex}; use super::rarity::{LocalizedCardRarityVec, LocalizedCardRarityIndex};
use super::set::{LocalizedCardSetVec, LocalizedCardSetIndex}; use super::set::{LocalizedCardSetVec, LocalizedCardSetIndex};
/// A parsed `globals.json` file from a Legends of Runeterra Core Bundle. /// A parsed `globals.json` file from a [Data Dragon] [Core Bundle].
/// ///
/// It contains a list of all vocabulary terms, keywords, regions, spell speeds, and rarities present in Legends of Runeterra. /// It contains [Vec]s of all vocabulary terms, keywords, regions, spell speeds, and rarities present in the game.
///
/// [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
/// [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct LocalizedGlobalsVecs { pub struct LocalizedGlobalsVecs {
/// Vocabulary terms.
#[serde(rename = "vocabTerms")] #[serde(rename = "vocabTerms")]
pub vocab_terms: LocalizedVocabTermVec, pub vocab_terms: LocalizedVocabTermVec,
/// Card keywords.
pub keywords: LocalizedCardKeywordVec, pub keywords: LocalizedCardKeywordVec,
/// Card regions.
pub regions: LocalizedCardRegionVec, pub regions: LocalizedCardRegionVec,
/// Spell speeds.
#[serde(rename = "spellSpeeds")] #[serde(rename = "spellSpeeds")]
pub spell_speeds: LocalizedSpellSpeedVec, pub spell_speeds: LocalizedSpellSpeedVec,
/// Card rarities.
pub rarities: LocalizedCardRarityVec, pub rarities: LocalizedCardRarityVec,
/// Card sets.
pub sets: LocalizedCardSetVec, pub sets: LocalizedCardSetVec,
} }
/// An instance of [LocalizedGlobalsVecs] which had its own fields indexed in [HashMap]s, using the respective identifiers as map keys. /// A parsed and indexed `globals.json` file from a [Data Dragon] [Core Bundle].
/// ///
/// It contains a indexed list of all vocabulary terms, keywords, regions, spell speeds, and rarities present in Legends of Runeterra. /// It is an instance of [LocalizedGlobalsVecs] which had its own fields indexed in [std::collections::HashMap]s, using the respective identifiers as map keys.
///
/// It contains indexes of all vocabulary terms, keywords, regions, spell speeds, and rarities present in the game.
///
/// [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
/// [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct LocalizedGlobalsIndexes { pub struct LocalizedGlobalsIndexes {
/// Vocabulary terms.
pub vocab_terms: LocalizedVocabTermIndex, pub vocab_terms: LocalizedVocabTermIndex,
/// Card keywords.
pub keywords: LocalizedCardKeywordIndex, pub keywords: LocalizedCardKeywordIndex,
/// Card regions.
pub regions: LocalizedCardRegionIndex, pub regions: LocalizedCardRegionIndex,
/// Spell speeds.
pub spell_speeds: LocalizedSpellSpeedIndex, pub spell_speeds: LocalizedSpellSpeedIndex,
/// Card rarities.
pub rarities: LocalizedCardRarityIndex, pub rarities: LocalizedCardRarityIndex,
/// Card sets.
pub sets: LocalizedCardSetIndex, pub sets: LocalizedCardSetIndex,
} }
@ -53,9 +73,9 @@ 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::Loading)?; .map_err(LoadingError::OpeningFile)?;
let data = serde_json::de::from_reader::<File, Self>(file) let data = serde_json::de::from_reader::<File, Self>(file)
.map_err(LoadingError::Parsing)?; .map_err(LoadingError::Deserializing)?;
Ok(data) Ok(data)
} }
} }
@ -113,7 +133,13 @@ impl From<LocalizedGlobalsVecs> for LocalizedGlobalsIndexes {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::LocalizedGlobalsVecs;
use crate::data::corebundle::keyword::LocalizedCardKeyword;
use crate::data::corebundle::rarity::LocalizedCardRarity;
use crate::data::corebundle::region::LocalizedCardRegion;
use crate::data::corebundle::set::LocalizedCardSet;
use crate::data::corebundle::speed::LocalizedSpellSpeed;
use crate::data::corebundle::vocabterm::LocalizedVocabTerm;
use crate::data::setbundle::keyword::CardKeyword; use crate::data::setbundle::keyword::CardKeyword;
use crate::data::setbundle::rarity::CardRarity; use crate::data::setbundle::rarity::CardRarity;
use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::region::CardRegion;
@ -170,21 +196,21 @@ mod tests {
"#).unwrap(), "#).unwrap(),
LocalizedGlobalsVecs { LocalizedGlobalsVecs {
vocab_terms: vec![ vocab_terms: vec![
vocabterm::LocalizedVocabTerm { LocalizedVocabTerm {
vocabterm: "Allegiance".to_string(), vocabterm: "Allegiance".to_string(),
name: "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(), description: "When you summon this, it gets its allegiance bonus if the top card of your deck matches its region.".to_string(),
} }
], ],
keywords: vec![ keywords: vec![
keyword::LocalizedCardKeyword { LocalizedCardKeyword {
keyword: CardKeyword::SpellOverwhelm, keyword: CardKeyword::SpellOverwhelm,
name: "Overwhelm".to_string(), name: "Overwhelm".to_string(),
description: "Inflicts damage beyond what would kill the target(s) to the enemy Nexus.".to_string(), description: "Inflicts damage beyond what would kill the target(s) to the enemy Nexus.".to_string(),
} }
], ],
regions: vec![ regions: vec![
region::LocalizedCardRegion { LocalizedCardRegion {
region: CardRegion::Noxus, region: CardRegion::Noxus,
name: "Noxus".to_string(), name: "Noxus".to_string(),
abbreviation: "NX".to_string(), abbreviation: "NX".to_string(),
@ -192,19 +218,19 @@ mod tests {
} }
], ],
spell_speeds: vec![ spell_speeds: vec![
speed::LocalizedSpellSpeed { LocalizedSpellSpeed {
spell_speed: SpellSpeed::Slow, spell_speed: SpellSpeed::Slow,
name: "Slow".to_string(), name: "Slow".to_string(),
} }
], ],
rarities: vec![ rarities: vec![
rarity::LocalizedCardRarity { LocalizedCardRarity {
rarity: CardRarity::Common, rarity: CardRarity::Common,
name: "COMMON".to_string(), name: "COMMON".to_string(),
} }
], ],
sets: vec![ sets: vec![
set::LocalizedCardSet { LocalizedCardSet {
set: CardSet::CallOfTheMountain, set: CardSet::CallOfTheMountain,
name: "Call of the Mountain".to_string(), 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(), icon_png: "http://dd.b.pvp.net/3_11_0/core/en_us/img/sets/set3_crispmip.png".to_string(),

View file

@ -1,4 +1,4 @@
//! This module defines [CoreKeyword]. //! Module defining structs representing localized card keywords.
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::keyword::CardKeyword; use crate::data::setbundle::keyword::CardKeyword;

View file

@ -1,8 +1,11 @@
//! This module defines the types used in Data Dragon [Core Bundles](https://developer.riotgames.com/docs/lor#data-dragon_core-bundles). //! Module defining the types used in [Data Dragon] [Core Bundle]s.
//!
//! [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 std::path::Path;
use super::anybundle::metadata::BundleMetadata; use super::anybundle::metadata::BundleMetadata;
use super::outcomes::{LoadingError, LoadingResult}; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
pub mod globals; pub mod globals;
pub mod vocabterm; pub mod vocabterm;
@ -13,8 +16,15 @@ pub mod rarity;
pub mod set; pub mod set;
/// A parsed [Core Bundle](https://developer.riotgames.com/docs/lor#data-dragon_core-bundles). /// A parsed [Data Dragon] [Core Bundle].
///
/// [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
/// [Core Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_core-bundles
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CoreBundle { pub struct CoreBundle {
/// The name of the root directory of the bundle.
pub name: String,
/// The contents of the `metadata.json` file. /// The contents of the `metadata.json` file.
pub metadata: BundleMetadata, pub metadata: BundleMetadata,
@ -31,15 +41,22 @@ impl CoreBundle {
.join("metadata.json") .join("metadata.json")
)?; )?;
let locale = metadata.locale().ok_or(LoadingError::Using)?; let name = bundle_path.file_name()
.ok_or(LoadingError::GettingBundleName)?
.to_str()
.ok_or(LoadingError::ConvertingBundleName)?
.to_string();
let globals = globals::LocalizedGlobalsVecs::load( let locale = metadata.locale()
&bundle_path .ok_or(LoadingError::GettingLocale)?;
let globals_path = &bundle_path
.join(&locale) .join(&locale)
.join("data") .join("data")
.join(format!("globals-{}.json", &locale)) .join(format!("globals-{}.json", &locale));
)?;
Ok(CoreBundle {metadata, globals}) let globals = globals::LocalizedGlobalsVecs::load(globals_path)?;
Ok(CoreBundle {name, metadata, globals})
} }
} }

View file

@ -1,4 +1,4 @@
//! This module defines [CoreRarity]. //! Module defining structs representing localized card rarities.
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::rarity::CardRarity; use crate::data::setbundle::rarity::CardRarity;

View file

@ -1,4 +1,4 @@
//! This module defines [CoreRegion]. //! Module defining structs representing localized card regions.
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::region::CardRegion; use crate::data::setbundle::region::CardRegion;

View file

@ -1,4 +1,4 @@
//! This module defines [CoreSet]. //! Module defining structs representing localized card sets.
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::set::CardSet; use crate::data::setbundle::set::CardSet;

View file

@ -1,4 +1,4 @@
//! This module defines [CoreSpellSpeed]. //! Module defining structs representing localized spell speeds.
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::speed::SpellSpeed; use crate::data::setbundle::speed::SpellSpeed;

View file

@ -1,4 +1,4 @@
//! This module defines [CoreVocabTerm]. //! Module defining structs representing localized vocabulary terms.
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;

View file

@ -1,6 +1,7 @@
//! This module contains the [corebundle] and [setbundle] submodules. //! Module containing Rust models for [Data Dragon] bundles.
//!
//! [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
pub mod corebundle; pub mod corebundle;
pub mod setbundle; pub mod setbundle;
pub mod anybundle; pub mod anybundle;
pub mod outcomes;

View file

@ -1,8 +0,0 @@
pub enum LoadingError {
Checking,
Loading(std::io::Error),
Parsing(serde_json::Error),
Using,
}
pub type LoadingResult<T> = Result<T, LoadingError>;

View file

@ -1,23 +1,27 @@
//! Module defining [CardArt]. //! Module defining [CardArt].
/// An art asset associated with a [super::Card]. /// The illustration of a [Card](super::card::Card), also referred to as an *art asset*.
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct CardArt { pub struct CardArt {
/// URL to the `.png` image of the rendered card. /// URL to the `.png` image of the rendered card.
/// ///
/// # Example /// ## Example
/// ///
/// `https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png` /// ```text
/// https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001.png
/// ```
/// ///
#[serde(rename = "gameAbsolutePath")] #[serde(rename = "gameAbsolutePath")]
pub card_png: String, pub card_png: String,
/// URL to the `.png` image of the full card art. /// URL to the `.png` image of the full card art.
/// ///
/// # Example /// ## Example
/// ///
/// `https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png` /// ```text
/// https://dd.b.pvp.net/latest/set1/en_us/img/cards/01DE001-full.png
/// ```
/// ///
#[serde(rename = "fullAbsolutePath")] #[serde(rename = "fullAbsolutePath")]
pub full_png: String, pub full_png: String,
@ -31,7 +35,9 @@ impl CardArt {
/// ///
/// # Example /// # Example
/// ///
/// `https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001.jpg` /// ```text
/// https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001.jpg
/// ```
/// ///
pub fn card_jpg(&self) -> String { pub fn card_jpg(&self) -> String {
self.card_png self.card_png
@ -45,7 +51,9 @@ impl CardArt {
/// ///
/// # Example /// # Example
/// ///
/// `https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001-full.jpg` /// ```text
/// https://poro.steffo.eu/set1-en_us/en_us/img/cards/01DE001-full.jpg
/// ```
/// ///
pub fn full_jpg(&self) -> String { pub fn full_jpg(&self) -> String {
self.full_png self.full_png

View file

@ -1,6 +1,8 @@
//! Module defining [Card]. //! Module defining [Card].
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::setbundle::subtype::CardSubtype;
use crate::data::setbundle::supertype::CardSupertype;
use super::r#type::CardType; use super::r#type::CardType;
use super::art::CardArt; use super::art::CardArt;
use super::keyword::CardKeyword; use super::keyword::CardKeyword;
@ -9,9 +11,9 @@ use super::region::CardRegion;
use super::speed::SpellSpeed; use super::speed::SpellSpeed;
use super::set::CardSet; use super::set::CardSet;
/// A single Legends of Runeterra card as represented in a `set.json` file. /// A single Legends of Runeterra card, as represented in a `set*.json` file.
/// ///
/// The information is represented in a developer-friendly manner, but it can be serialized and deserialized via [serde] in the exact same format used in Data Dragon. /// The data is available in a developer-friendly interface, but nevertheless it can be serialized and deserialized via [serde] in the exact same format used in the `set*.json` files.
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Card { pub struct Card {
/// Unique seven-character identifier of the card. /// Unique seven-character identifier of the card.
@ -21,21 +23,23 @@ pub struct Card {
/// Localized name of the card. /// Localized name of the card.
pub name: String, pub name: String,
/// The [CardType] of the card. /// The type of the card.
/// ///
/// The `r#` prefix is required by the Rust syntax, since `type` is a reserved keyword. /// Since `type` in Rust is a reserved keyword, accessing this field requires [prefixing it with `r#`](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html).
#[serde(rename = "type")] #[serde(rename = "type")]
pub r#type: CardType, pub r#type: CardType,
/// The [CardSet] the card belongs to. /// The release set the card belongs to.
pub set: CardSet, pub set: CardSet,
/// [CardRarity] of the card. /// The rarity of the card.
#[serde(rename = "rarityRef")] #[serde(rename = "rarityRef")]
pub rarity: CardRarity, pub rarity: CardRarity,
/// Whether the card is collectible or not.
///
/// If `true`, the card can be found in chests, crafted, or used in decks. /// If `true`, the card can be found in chests, crafted, or used in decks.
/// If `false`, the card is not available for direct use, as it is probably created by another card. /// If `false`, the card is not available for direct use, and is probably created by another card, or available only in special occasions, such as Lab matches.
pub collectible: bool, pub collectible: bool,
/// Regions this card belongs to. /// Regions this card belongs to.
@ -77,17 +81,17 @@ pub struct Card {
/// Localized name of the [SpellSpeed] of the card. /// Localized name of the [SpellSpeed] of the card.
/// ///
/// For serialization purposes only, use the [method with the same name](Card::localized_spell_speed()] instead! /// **For serialization purposes only**, use the [SpellSpeed::localized] instead!
#[serde(rename = "spellSpeed")] #[serde(rename = "spellSpeed")]
pub(crate) localized_spell_speed: String, pub(crate) localized_spell_speed: String,
/// [Vec] of [CardKeyword]s of the card. /// [Vec] of [CardKeyword]s of the card, such as [*Overwhelm*](CardKeyword::Overwhelm) or [*Focus*](CardKeyword::Focus).
#[serde(rename = "keywordRefs")] #[serde(rename = "keywordRefs")]
pub keywords: Vec<CardKeyword>, pub keywords: Vec<CardKeyword>,
/// [Vec] of localized names of [CardKeyword]s of the card. /// [Vec] of localized names of [CardKeyword]s of the card.
/// ///
/// For serialization purposes only, use the [method with the same name](Card::localized_keywords()] instead! /// **For serialization purposes only**, use the [CardKeyword::localized] instead!
#[serde(rename = "keywords")] #[serde(rename = "keywords")]
pub(crate) localized_keywords: Vec<String>, pub(crate) localized_keywords: Vec<String>,
@ -120,6 +124,8 @@ pub struct Card {
/// [Vec] with [Card::name]s of other cards associated with this one. /// [Vec] with [Card::name]s of other cards associated with this one.
/// ///
/// Sometimes, it may be missing some references. /// Sometimes, it may be missing some references.
///
/// **For serialization purposes only**, use [Card::associated_cards] instead!
#[serde(rename = "associatedCards")] #[serde(rename = "associatedCards")]
pub(crate) associated_card_names_localized: Vec<String>, pub(crate) associated_card_names_localized: Vec<String>,
@ -131,38 +137,33 @@ pub struct Card {
#[serde(rename = "artistName")] #[serde(rename = "artistName")]
pub artist_name: String, pub artist_name: String,
/// The subtypes the card has, such as `"PORO"`. /// The subtypes the card belongs to, such as *Poro* or *Yordle*.
/// pub subtypes: Vec<CardSubtype>,
/// Beware of Riot's inconsistent capitalization!
///
/// TODO: Make this a enum.
pub subtypes: Vec<String>,
/// The supertype the card belongs to, such as `"Champion"`. /// The supertype the card belongs to, such as *Champion*.
/// pub supertype: CardSupertype,
/// Beware of Riot's inconsistent capitalization!
///
/// TODO: Make this a enum.
pub supertype: String,
} }
impl Card { impl Card {
/// Get references to the cards associated with this one, given an [HashMap] of cards indexed by code. /// Get references to the cards associated with this one, given an [HashMap] of cards indexed by code.
pub fn associated_cards<'c, 'hm: 'c>(&'c self, hashmap: &'hm HashMap<String, Card>) -> impl Iterator<Item=Option<&'hm Card>> + 'c { pub fn associated_cards<'c, 'hm: 'c>(&'c self, index: &'hm CardIndex) -> impl Iterator<Item=Option<&'hm Card>> + 'c {
self.associated_card_codes.iter().map(|r| hashmap.get(r)) self.associated_card_codes.iter().map(|r| index.get(r))
} }
/// Get a reference to the first [CardArt] of the card. /// Get a reference to the first [CardArt] of the card.
/// ///
/// # Panics /// Equivalent to calling [CardArt].[art](super::art::CardArt::art).[get(0)]([T]::get).
/// pub fn main_art(&self) -> Option<&CardArt> {
/// If the card has no associated [CardArt]. self.art.get(0)
pub fn main_art(&self) -> &CardArt {
self.art.get(0).expect("card to have at least one art asset")
} }
} }
/// An index of [Card]s, with [Card::code]s as keys.
pub type CardIndex = HashMap<String, Card>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -2,18 +2,18 @@
use crate::data::corebundle::keyword::{LocalizedCardKeyword, LocalizedCardKeywordIndex}; use crate::data::corebundle::keyword::{LocalizedCardKeyword, LocalizedCardKeywordIndex};
/// A keyword which cards can have. /// A keyword which [Card](super::card::Card)s can have.
/// ///
/// Since more keywords will probably be added in the future, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). /// Since more keywords will probably be added in the future, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute).
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum CardKeyword { pub enum CardKeyword {
/// Like [CardKeyword::Overwhelm], but on [super::CardType::Spell]s. /// Like [Overwhelm](CardKeyword::Overwhelm), but on [Spell](super::type::CardType::Spell)s.
/// ///
/// > Inflicts damage beyond what would kill the target(s) to the enemy Nexus. /// > Inflicts damage beyond what would kill the target(s) to the enemy Nexus.
SpellOverwhelm, SpellOverwhelm,
/// [super::SpellSpeed::Burst]. /// [Burst](super::speed::SpellSpeed::Burst).
/// ///
/// > Can be played whenever you may act. Happens instantly and allows you to continue to play other cards. /// > Can be played whenever you may act. Happens instantly and allows you to continue to play other cards.
Burst, Burst,
@ -29,7 +29,7 @@ pub enum CardKeyword {
#[serde(rename = "PlaySkillMark")] #[serde(rename = "PlaySkillMark")]
OnPlay, OnPlay,
/// [super::CardType::Landmark]. /// [Landmark](super::type::CardType::Landmark).
/// ///
/// > Landmarks take up a space on the board. They can't attack, block, or take damage. /// > Landmarks take up a space on the board. They can't attack, block, or take damage.
#[serde(rename = "LandmarkVisualOnly")] #[serde(rename = "LandmarkVisualOnly")]
@ -59,7 +59,7 @@ pub enum CardKeyword {
/// Focus. /// Focus.
/// ///
/// Used to disambiguate between Burst and Focus with [super::SpellSpeed::Burst]. /// Used to disambiguate between Burst and Focus with [SpellSpeed::Burst](super::speed::SpellSpeed::Burst).
/// ///
/// > Can be played outside combat or when no other spells or skills are pending. Happens instantly and allows you to continue to play other cards. /// > Can be played outside combat or when no other spells or skills are pending. Happens instantly and allows you to continue to play other cards.
Focus, Focus,
@ -101,7 +101,7 @@ pub enum CardKeyword {
/// ??? /// ???
BandleCity, BandleCity,
/// [super::SpellSpeed::Fast]. /// [Fast](super::speed::SpellSpeed::Fast).
/// ///
/// > Can be played whenever you may act. Happens after your opponent has a chance to react. /// > Can be played whenever you may act. Happens after your opponent has a chance to react.
Fast, Fast,
@ -137,7 +137,9 @@ pub enum CardKeyword {
/// > Completely removed from the game. Doesn't cause Last Breath and can't be revived. /// > Completely removed from the game. Doesn't cause Last Breath and can't be revived.
Obliterate, Obliterate,
/// Imbue, an unused keyword. /// Imbue.
///
/// Currently unused.
/// ///
/// > These abilities trigger when you resolve a spell. /// > These abilities trigger when you resolve a spell.
Imbue, Imbue,
@ -181,7 +183,7 @@ pub enum CardKeyword {
/// > A unit's spell-like effect that allows enemy reactions. /// > A unit's spell-like effect that allows enemy reactions.
Skill, Skill,
/// Plunder, /// Plunder.
/// ///
/// > A card triggers its plunder ability when played if you damaged the enemy Nexus this round. /// > A card triggers its plunder ability when played if you damaged the enemy Nexus this round.
Plunder, Plunder,

View file

@ -1,9 +1,12 @@
//! This module defines the types used in Data Dragon [Set Bundles](https://developer.riotgames.com/docs/lor#data-dragon_set-bundles). //! Module defining the types used in [Data Dragon] [Set Bundle]s.
//!
//! [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::fs::File;
use std::path::Path; use std::path::Path;
use super::anybundle::metadata::BundleMetadata; use super::anybundle::metadata::BundleMetadata;
use super::outcomes::{LoadingError, LoadingResult}; use crate::data::anybundle::outcomes::{LoadingError, LoadingResult};
pub mod card; pub mod card;
pub mod art; pub mod art;
@ -13,15 +16,23 @@ pub mod region;
pub mod set; pub mod set;
pub mod speed; pub mod speed;
pub mod keyword; pub mod keyword;
pub mod subtype;
pub mod supertype;
/// A parsed [Set Bundle](https://developer.riotgames.com/docs/lor#data-dragon_set-bundles). /// A parsed [Data Dragon] [Set Bundle].
///
/// [Data Dragon]: https://developer.riotgames.com/docs/lor#data-dragon
/// [Set Bundle]: https://developer.riotgames.com/docs/lor#data-dragon_set-bundles
pub struct SetBundle { pub struct SetBundle {
/// The contents of the `metadata.json` file. /// The contents of the `metadata.json` file.
pub metadata: BundleMetadata, pub metadata: BundleMetadata,
/// The contents of the `[locale]/data/globals-[locale].json` file. /// The contents of the `[locale]/data/globals-[locale].json` file.
pub cards: Vec<card::Card>, pub cards: Vec<card::Card>,
/// The name of the root directory of the bundle.
pub name: String,
} }
@ -33,21 +44,32 @@ impl SetBundle {
.join("metadata.json") .join("metadata.json")
)?; )?;
let locale = metadata.locale().ok_or(LoadingError::Using)?; let locale = metadata.locale()
.ok_or(LoadingError::GettingLocale)?;
let mut filename = bundle_path.file_name().ok_or(LoadingError::Checking)?.to_os_string(); let name = bundle_path.file_name()
filename.push(".json"); .ok_or(LoadingError::GettingBundleName)?;
let data_path = {
let mut json_filename = name.to_os_string();
json_filename.push(".json");
let cards = File::open(
&bundle_path &bundle_path
.join(&locale) .join(&locale)
.join("data") .join("data")
.join(filename) .join(&json_filename)
).map_err(LoadingError::Loading)?; };
let name = name.to_str()
.ok_or(LoadingError::ConvertingBundleName)?
.to_string();
let cards = File::open(data_path)
.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::Parsing)?; .map_err(LoadingError::Deserializing)?;
Ok(SetBundle {metadata, cards}) Ok(SetBundle {metadata, cards, name})
} }
} }

View file

@ -2,22 +2,22 @@
use crate::data::corebundle::rarity::{LocalizedCardRarity, LocalizedCardRarityIndex}; use crate::data::corebundle::rarity::{LocalizedCardRarity, LocalizedCardRarityIndex};
/// A possible [super::Card] rarity. /// A possible [Card](super::card::Card) rarity.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum CardRarity { pub enum CardRarity {
/// The card has no rarity, as it probably is not collectible. /// The card has no rarity, as it probably is not [collectible](super::card::Card::collectible).
None, None,
/// A common card. /// A common (green triangle) card.
Common, Common,
/// A rare card. /// A rare (blue square) card.
Rare, Rare,
/// An epic card. /// An epic (purple pentagon) card.
Epic, Epic,
/// A champion. /// A champion (orange hexagon) card, sometimes referred to as *Legendary*.
Champion, Champion,
} }

View file

@ -2,7 +2,7 @@
use crate::data::corebundle::region::{LocalizedCardRegion, LocalizedCardRegionIndex}; use crate::data::corebundle::region::{LocalizedCardRegion, LocalizedCardRegionIndex};
/// A region to which [super::Card]s can belong to. /// A region to which [Card](super::card::Card)s can belong to.
/// ///
/// Since more regions might be added in the future, especially Origin ones, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). /// Since more regions might be added in the future, especially Origin ones, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute).
#[non_exhaustive] #[non_exhaustive]

View file

@ -2,7 +2,7 @@
use crate::data::corebundle::set::{LocalizedCardSet, LocalizedCardSetIndex}; use crate::data::corebundle::set::{LocalizedCardSet, LocalizedCardSetIndex};
/// The release set a [super::Card] may belong to. /// The release set a [Card](super::card::Card) may belong to.
/// ///
/// Since more sets will definitely be added in the future, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). /// Since more sets will definitely be added in the future, this enum is [non_exaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute).
#[non_exhaustive] #[non_exhaustive]
@ -32,7 +32,7 @@ pub enum CardSet {
#[serde(rename = "Set6")] #[serde(rename = "Set6")]
Worldwalker, Worldwalker,
/// Events, with cards released "outside" a set. /// Events, cards released "outside" a set.
#[serde(rename = "SetEvent")] #[serde(rename = "SetEvent")]
Events, Events,

View file

@ -2,7 +2,7 @@
use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex}; use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex};
/// A possible spell speed. /// A possible [Spell](super::type::CardType::Spell) speed.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SpellSpeed { pub enum SpellSpeed {
/// Non-spell cards have this speed. /// Non-spell cards have this speed.

View file

@ -0,0 +1,8 @@
//! Module defining [CardSubtype].
/// A subtype of a [Card](super::card::Card), such as *Poro* or *Yordle*.
///
/// Capitalization of the various subtypes is inconsistent.
///
/// TODO: As soon as all subtypes are known, make this a enum.
pub type CardSubtype = String;

View file

@ -0,0 +1,8 @@
//! Module defining [CardSupertype].
/// A supertype of a [Card](super::card::Card), such as *Champion*.
///
/// Capitalization of the various supertypes is inconsistent.
///
/// TODO: As soon as all supertypes are known, make this a enum.
pub type CardSupertype = String;

View file

@ -1,7 +1,7 @@
//! Module defining [CardType]. //! Module defining [CardType].
/// A possible 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).
#[non_exhaustive] #[non_exhaustive]
@ -9,14 +9,21 @@
pub enum CardType { pub enum CardType {
/// A spell. /// A spell.
Spell, Spell,
/// An unit: either a minion, or a champion. /// An unit: either a minion, or a champion.
/// Champions have their `supertype` set to `Champion`, and their `rarity` set to `Champion` as well. ///
/// Champions have their [supertype](super::card::Card::supertype) set to `Champion`, and their [rarity](super::card::Card::rarity) set to [CardRarity::Champion](super::rarity::CardRarity::Champion) as well.
Unit, Unit,
/// An ability triggered by an unit.
/// An ability triggered by an [Unit](CardType::Unit).
Ability, Ability,
/// A landmark. /// A landmark.
Landmark, Landmark,
/// A trap or boon.
/// An autoplaying card: either a trap or boon.
///
/// Disambiguate between the two using [CardKeyword::Trap](super::keyword::CardKeyword::Trap) and [CardKeyword::Boon](super::keyword::CardKeyword::Boon).
Trap, Trap,
/// Unsupported card type. /// Unsupported card type.

View file

@ -1,7 +1,21 @@
//! Crate providing parsing, indexing, and displaying utilities for Legends of Runeterra data files.
//!
//! # Features
//!
//! - `search`: Adds a search engine based on [tantivy] for Legends of Runeterra data.
//! - `telegram`: Adds a [Telegram bot](https://core.telegram.org/bots/api) based on [teloxide] for Legends of Runeterra data.
//!
//! # Legal
//!
//! [patched_porobot](self) 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.
#![warn(missing_docs)]
#![doc(html_logo_url = "https://raw.githubusercontent.com/Steffo99/patched-porobot/main/icon.png")]
pub mod data; pub mod data;
#[cfg(feature = "search")] // #[cfg(feature = "search")]
pub mod search; // pub mod search;
#[cfg(feature = "telegram")] // #[cfg(feature = "telegram")]
pub mod telegram; // pub mod telegram;

View file

@ -6,7 +6,7 @@ use tantivy::query::{QueryParser, QueryParserError};
use tantivy::schema::{Schema, TextOptions}; use tantivy::schema::{Schema, TextOptions};
use tantivy::tokenizer::TextAnalyzer; use tantivy::tokenizer::TextAnalyzer;
use itertools::Itertools; use itertools::Itertools;
use crate::data::corebundle::CoreBundle; use crate::data::corebundle::globals::LocalizedGlobalsIndexes;
use crate::data::setbundle::r#type::CardType; use crate::data::setbundle::r#type::CardType;
use crate::data::setbundle::card::Card; use crate::data::setbundle::card::Card;
@ -93,7 +93,7 @@ pub fn card_schema() -> Schema {
/// Create a new [tantivy::Document] using a [Card] in a specific [locale](MappedGlobals] as base. /// Create a new [tantivy::Document] using a [Card] in a specific [locale](MappedGlobals] as base.
pub fn card_to_document(schema: &Schema, cb: &CoreBundle, card: Card) -> Document { pub fn card_to_document(schema: &Schema, globals: &LocalizedGlobalsIndexes, card: Card) -> Document {
use tantivy::*; use tantivy::*;
let f_code = schema.get_field("code").expect("schema to have a 'code' field"); let f_code = schema.get_field("code").expect("schema to have a 'code' field");
@ -122,7 +122,7 @@ pub fn card_to_document(schema: &Schema, cb: &CoreBundle, card: Card) -> Documen
CardType::Ability => "Ability", CardType::Ability => "Ability",
CardType::Landmark => "Landmark", CardType::Landmark => "Landmark",
CardType::Trap => "Trap", CardType::Trap => "Trap",
CardType::Unsupported => "" CardType::Unsupported => "Unknown",
}; };
doc!( doc!(
@ -130,17 +130,17 @@ pub fn card_to_document(schema: &Schema, cb: &CoreBundle, card: Card) -> Documen
f_name => card.name, f_name => card.name,
f_type => c_type, f_type => c_type,
f_set => card.set f_set => card.set
.localized(&cb.globals.sets) .localized(&globals.sets)
.map(|cs| cs.name.to_owned()) .map(|cs| cs.name.to_owned())
.unwrap_or_else(String::new), .unwrap_or_else(String::new),
f_rarity => card.rarity f_rarity => card.rarity
.localized(&cb.globals.rarities) .localized(&globals.rarities)
.map(|cr| cr.name.to_owned()) .map(|cr| cr.name.to_owned())
.unwrap_or_else(String::new), .unwrap_or_else(String::new),
f_collectible => if card.collectible {1u64} else {0u64}, f_collectible => if card.collectible {1u64} else {0u64},
f_regions => card.regions.iter() f_regions => card.regions.iter()
.map(|region| region .map(|region| region
.localized(&cb.globals.regions) .localized(&globals.regions)
.map(|cr| cr.name.to_owned()) .map(|cr| cr.name.to_owned())
.unwrap_or_else(String::new) .unwrap_or_else(String::new)
).join(" "), ).join(" "),
@ -148,12 +148,12 @@ pub fn card_to_document(schema: &Schema, cb: &CoreBundle, card: Card) -> Documen
f_cost => card.cost, f_cost => card.cost,
f_health => card.health, f_health => card.health,
f_spellspeed => card.spell_speed f_spellspeed => card.spell_speed
.localized(&cb.globals.spell_speeds) .localized(&globals.spell_speeds)
.map(|ss| ss.name.to_owned()) .map(|ss| ss.name.to_owned())
.unwrap_or_else(String::new), .unwrap_or_else(String::new),
f_keywords => card.keywords.iter() f_keywords => card.keywords.iter()
.map(|keyword| keyword .map(|keyword| keyword
.localized(&cb.globals.keywords) .localized(&globals.keywords)
.map(|ck| ck.name.to_owned()) .map(|ck| ck.name.to_owned())
.unwrap_or_else(String::new)) .unwrap_or_else(String::new))
.join(" "), .join(" "),
@ -169,9 +169,9 @@ pub fn card_to_document(schema: &Schema, cb: &CoreBundle, card: Card) -> Documen
/// Stage all [tantivy::Document]s generated from [Card]s contained in the passed [Vec] for write on a [tantivy::Index] via the given [tantivy::IndexWriter]. /// Stage all [tantivy::Document]s generated from [Card]s contained in the passed [Vec] for write on a [tantivy::Index] via the given [tantivy::IndexWriter].
pub fn cards_to_index(writer: IndexWriter, schema: Schema, locale: MappedGlobals, cards: Vec<Card>) -> tantivy::Result<()> { pub fn cards_to_index(writer: IndexWriter, schema: Schema, globals: &LocalizedGlobalsIndexes, cards: Vec<Card>) -> tantivy::Result<()> {
for card in cards { for card in cards {
writer.add_document(card_to_document(&schema, &locale, card))?; writer.add_document(card_to_document(&schema, &globals, card))?;
}; };
Ok(()) Ok(())
} }

View file

@ -7,10 +7,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use itertools::Itertools; use itertools::Itertools;
use teloxide::utils::html::escape; use teloxide::utils::html::escape;
use crate::data::setbundle::card::Card;
/// Render a [Card] to a [String] formatted with [Telegram Bot HTML](https://core.telegram.org/bots/api#html-style). /// Render a [Card] to a [String] formatted with [Telegram Bot HTML](https://core.telegram.org/bots/api#html-style).
pub fn display_card(card: &, mg: &MappedGlobals) -> String { pub fn display_card(card: &Card, mg: &MappedGlobals) -> String {
let title = format!(r#"<a href="{}"><b><i>{}</b></i></a>"#, &card.main_art().card_png, escape(&card.name)); let title = format!(r#"<a href="{}"><b><i>{}</b></i></a>"#, &card.main_art().card_png, escape(&card.name));
let stats = match &card.r#type { let stats = match &card.r#type {