1use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SchemaResponse {
14 pub agents: Option<serde_json::Value>,
15 pub collectibles: Option<serde_json::Value>,
16 pub collections: Vec<Collection>,
17 pub containers: Option<serde_json::Value>,
18 pub custom_stickers: Option<serde_json::Value>,
19 pub highlight_reels: Option<serde_json::Value>,
20 pub keychains: Option<serde_json::Value>,
21 pub music_kits: Option<serde_json::Value>,
22 pub rarities: Option<serde_json::Value>,
23 pub stickers: Option<serde_json::Value>,
24 pub weapons: HashMap<String, Weapon>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Collection {
30 pub key: String,
31 pub name: String,
32 pub has_souvenir: Option<bool>,
33 pub has_crate: Option<bool>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct Weapon {
39 pub name: String,
40 pub sticker_amount: u32,
41 #[serde(rename = "type")]
42 pub weapon_type: String,
43 pub faction: Option<String>,
44 pub paints: HashMap<String, Paint>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Paint {
50 pub index: u32,
51 pub max: f64,
52 pub min: f64,
53 pub rarity: u32,
54 pub name: String,
55 pub collection: Option<String>,
56 pub image: String,
57 pub stattrak: Option<bool>,
58 pub souvenir: Option<bool>,
59 pub normal_prices: Vec<u64>,
61 pub normal_volume: Vec<u64>,
63 pub stattrak_prices: Option<Vec<u64>>,
65 pub stattrak_volume: Option<Vec<u64>>,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum WearCondition {
72 FactoryNew = 0,
73 MinimalWear = 1,
74 FieldTested = 2,
75 WellWorn = 3,
76 BattleScarred = 4,
77}
78
79impl WearCondition {
80 pub fn index(self) -> usize {
82 self as usize
83 }
84
85 pub fn as_str(self) -> &'static str {
87 match self {
88 WearCondition::FactoryNew => "Factory New",
89 WearCondition::MinimalWear => "Minimal Wear",
90 WearCondition::FieldTested => "Field-Tested",
91 WearCondition::WellWorn => "Well-Worn",
92 WearCondition::BattleScarred => "Battle-Scarred",
93 }
94 }
95}
96
97impl Paint {
98 pub fn price_for_wear(&self, wear: WearCondition) -> Option<u64> {
100 self.normal_prices.get(wear.index()).copied()
101 }
102
103 pub fn volume_for_wear(&self, wear: WearCondition) -> Option<u64> {
105 self.normal_volume.get(wear.index()).copied()
106 }
107
108 pub fn stattrak_price_for_wear(&self, wear: WearCondition) -> Option<u64> {
110 self.stattrak_prices
111 .as_ref()
112 .and_then(|prices| prices.get(wear.index()).copied())
113 }
114
115 pub fn stattrak_volume_for_wear(&self, wear: WearCondition) -> Option<u64> {
117 self.stattrak_volume
118 .as_ref()
119 .and_then(|volumes| volumes.get(wear.index()).copied())
120 }
121
122 pub fn has_stattrak(&self) -> bool {
124 self.stattrak.unwrap_or(false)
125 }
126
127 pub fn has_souvenir(&self) -> bool {
129 self.souvenir.unwrap_or(false)
130 }
131
132 pub fn float_range(&self) -> (f64, f64) {
134 (self.min, self.max)
135 }
136}
137
138impl Weapon {
139 pub fn get_paint_by_index(&self, index: u32) -> Option<&Paint> {
141 self.paints.values().find(|paint| paint.index == index)
142 }
143
144 pub fn get_paint_by_name(&self, name: &str) -> Option<&Paint> {
146 self.paints
147 .values()
148 .find(|paint| paint.name.to_lowercase() == name.to_lowercase())
149 }
150
151 pub fn paints_by_rarity(&self) -> Vec<&Paint> {
153 let mut paints: Vec<&Paint> = self.paints.values().collect();
154 paints.sort_by(|a, b| b.rarity.cmp(&a.rarity));
155 paints
156 }
157}
158
159impl SchemaResponse {
160 pub fn get_weapon(&self, weapon_id: &str) -> Option<&Weapon> {
162 self.weapons.get(weapon_id)
163 }
164
165 pub fn get_weapon_by_name(&self, name: &str) -> Option<(String, &Weapon)> {
167 self.weapons
168 .iter()
169 .find(|(_, weapon)| weapon.name.to_lowercase() == name.to_lowercase())
170 .map(|(id, weapon)| (id.clone(), weapon))
171 }
172
173 pub fn weapons_by_name(&self) -> Vec<(String, &Weapon)> {
175 let mut weapons: Vec<(String, &Weapon)> =
176 self.weapons.iter().map(|(id, weapon)| (id.clone(), weapon)).collect();
177 weapons.sort_by(|a, b| a.1.name.cmp(&b.1.name));
178 weapons
179 }
180
181 pub fn find_paint(&self, weapon_name: &str, paint_name: &str) -> Option<(&Weapon, &Paint)> {
183 self.get_weapon_by_name(weapon_name)
184 .and_then(|(_, weapon)| {
185 weapon.get_paint_by_name(paint_name)
186 .map(|paint| (weapon, paint))
187 })
188 }
189}