use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::common::RivenType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Riven {
pub id: String,
pub slug: String,
#[serde(rename = "gameRef")]
pub game_ref: String,
pub group: String,
#[serde(rename = "rivenType")]
pub riven_type: RivenType,
pub disposition: f32,
#[serde(rename = "reqMasteryRank", default)]
pub mastery_rank: Option<u32>,
#[serde(default)]
pub i18n: HashMap<String, RivenTranslation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RivenTranslation {
pub name: String,
pub icon: String,
#[serde(default)]
pub thumb: Option<String>,
#[serde(rename = "wikiLink", default)]
pub wiki_link: Option<String>,
}
impl Riven {
pub fn name(&self) -> &str {
self.i18n.get("en").map(|t| t.name.as_str()).unwrap_or("")
}
pub fn name_localized(&self, lang: &str) -> &str {
self.i18n
.get(lang)
.or_else(|| self.i18n.get("en"))
.map(|t| t.name.as_str())
.unwrap_or("")
}
pub fn icon(&self) -> Option<&str> {
self.i18n.get("en").map(|t| t.icon.as_str())
}
pub fn wiki_link(&self) -> Option<&str> {
self.i18n.get("en").and_then(|t| t.wiki_link.as_deref())
}
pub fn disposition_tier(&self) -> u8 {
match self.disposition {
d if d < 0.7 => 1,
d if d < 0.9 => 2,
d if d < 1.1 => 3,
d if d < 1.3 => 4,
_ => 5,
}
}
pub fn is_melee(&self) -> bool {
matches!(self.riven_type, RivenType::Melee | RivenType::Zaw)
}
pub fn is_ranged(&self) -> bool {
matches!(
self.riven_type,
RivenType::Rifle | RivenType::Pistol | RivenType::Shotgun | RivenType::Kitgun
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RivenAttribute {
pub id: String,
pub slug: String,
#[serde(rename = "gameRef")]
pub game_ref: String,
pub group: String,
pub prefix: String,
pub suffix: String,
#[serde(rename = "exclusiveTo", default)]
pub exclusive_to: Option<Vec<RivenType>>,
#[serde(rename = "positiveIsNegative", default)]
pub positive_is_negative: Option<bool>,
#[serde(rename = "positiveOnly", default)]
pub positive_only: Option<bool>,
#[serde(rename = "negativeOnly", default)]
pub negative_only: Option<bool>,
#[serde(default)]
pub unit: Option<String>,
#[serde(default)]
pub i18n: HashMap<String, RivenAttributeTranslation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RivenAttributeTranslation {
pub name: String,
pub icon: String,
#[serde(default)]
pub thumb: Option<String>,
}
impl RivenAttribute {
pub fn name(&self) -> &str {
self.i18n.get("en").map(|t| t.name.as_str()).unwrap_or("")
}
pub fn name_localized(&self, lang: &str) -> &str {
self.i18n
.get(lang)
.or_else(|| self.i18n.get("en"))
.map(|t| t.name.as_str())
.unwrap_or("")
}
pub fn is_inverted(&self) -> bool {
self.positive_is_negative.unwrap_or(false)
}
pub fn is_restricted(&self) -> bool {
self.exclusive_to.as_ref().is_some_and(|v| !v.is_empty())
}
pub fn applies_to(&self, riven_type: RivenType) -> bool {
match &self.exclusive_to {
None => true,
Some(types) => types.is_empty() || types.contains(&riven_type),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_riven() -> Riven {
Riven {
id: "test-id".to_string(),
slug: "braton".to_string(),
game_ref: "/Lotus/Weapons/Tenno/Rifle/Braton".to_string(),
group: "rifle".to_string(),
riven_type: RivenType::Rifle,
disposition: 1.45,
mastery_rank: Some(0),
i18n: HashMap::from([(
"en".to_string(),
RivenTranslation {
name: "Braton".to_string(),
icon: "/icons/braton.png".to_string(),
thumb: None,
wiki_link: Some("https://warframe.fandom.com/wiki/Braton".to_string()),
},
)]),
}
}
#[test]
fn test_riven_name() {
let riven = make_riven();
assert_eq!(riven.name(), "Braton");
}
#[test]
fn test_disposition_tier() {
let mut riven = make_riven();
riven.disposition = 0.5;
assert_eq!(riven.disposition_tier(), 1);
riven.disposition = 0.8;
assert_eq!(riven.disposition_tier(), 2);
riven.disposition = 1.0;
assert_eq!(riven.disposition_tier(), 3);
riven.disposition = 1.2;
assert_eq!(riven.disposition_tier(), 4);
riven.disposition = 1.5;
assert_eq!(riven.disposition_tier(), 5);
}
#[test]
fn test_weapon_type() {
let riven = make_riven();
assert!(riven.is_ranged());
assert!(!riven.is_melee());
}
}