achievement.rs
1 use std::collections::HashMap; 2 use chrono::{DateTime, Utc}; 3 use serde::{Deserialize, Serialize}; 4 use crate::battlenet::platform::data::starcraft2::profile::metadata::AchievementMetadata; 5 use crate::battlenet::platform::data::starcraft2::profile::profile::EarnedAchievement; 6 7 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] 8 pub struct Sc2Achievement 9 { 10 pub description: String, 11 pub displayOrder: u64, 12 pub id: u64, 13 pub name: String, 14 pub points: u64, 15 pub unlocked: bool, 16 pub unlockedTimestamp: Option<DateTime<Utc>>, 17 } 18 19 impl From<AchievementMetadata> for Sc2Achievement 20 { 21 fn from(value: AchievementMetadata) -> Self 22 { 23 return Self 24 { 25 description: value.description.clone(), 26 displayOrder: value.uiOrderHint, 27 28 id: value.id.parse::<u64>() 29 .unwrap_or_default(), 30 31 name: value.title.clone(), 32 points: value.points, 33 ..Default::default() 34 }; 35 } 36 } 37 38 impl Sc2Achievement 39 { 40 /** 41 Process the achievements metadata returned by the Profile/Static endpoint. 42 43 Returns a map pairing each `Sc2Achievement` with its associated category id. 44 */ 45 pub fn processMetadata(metadata: Vec<AchievementMetadata>) -> HashMap<String, Self> 46 { 47 let mut map = HashMap::new(); 48 49 for metaChievo in metadata 50 { 51 map.insert( 52 metaChievo.categoryId.clone(), 53 Self::from(metaChievo), 54 ); 55 } 56 57 return map; 58 } 59 60 pub fn update(&mut self, value: EarnedAchievement) 61 { 62 self.unlocked = value.isComplete; 63 self.unlockedTimestamp = DateTime::from_timestamp(value.completionDate as i64, 0); 64 } 65 } 66 67 #[cfg(test)] 68 mod tests 69 { 70 use std::str::FromStr; 71 72 use super::*; 73 74 const JsonPayload: &str = r#" 75 [ 76 { 77 "categoryId":"1", 78 "chainAchievementIds":["2"], 79 "chainRewardSize":1, 80 "criteriaIds":["3"], 81 "description":"Achievement id 4 description", 82 "flags":2, 83 "id":"4", 84 85 "imageUrl":"someurl", 86 87 "isChained":false, 88 "points":0, 89 "title":"Achievement id 4", 90 "uiOrderHint":8 91 }, 92 93 { 94 "categoryId":"5", 95 "chainAchievementIds":["6"], 96 "chainRewardSize":0, 97 "criteriaIds":["7"], 98 "description":"Achievement id 8 description", 99 "flags":2, 100 "id":"8", 101 "imageUrl":"someurl", 102 "isChained":false, 103 "points":0, 104 "title":"Achievement id 8", 105 "uiOrderHint":12 106 }, 107 108 { 109 "categoryId":"9", 110 "chainAchievementIds":["10"], 111 "chainRewardSize":0, 112 "criteriaIds":[ 113 "11", 114 "12", 115 "13" 116 ], 117 "description":"Achievement id 14 description", 118 "flags":0, 119 "id":"14", 120 "imageUrl":"someurl", 121 "isChained":false, 122 "points":10, 123 "title":"Achievement id 14", 124 "uiOrderHint":27 125 } 126 ] 127 "#; 128 129 const EarnedJson: &str = r#" 130 { 131 "achievementId":"9", 132 "completionDate":1765083932, 133 "numCompletedAchievementsInSeries":10, 134 "totalAchievementsInSeries":10, 135 "isComplete":true, 136 "inProgress":false, 137 "criteria":[ 138 {"criterionId":"25","earned":{"quantity":1,"startTime":1}} 139 ] 140 } 141 "#; 142 143 #[test] 144 fn processMetadata() 145 { 146 let metadata = serde_json::from_str(JsonPayload); 147 148 assert!(metadata.is_ok()); 149 let metadata = metadata.unwrap(); 150 151 let achievements = Sc2Achievement::processMetadata(metadata); 152 assert!(!achievements.is_empty()); 153 assert_eq!(achievements.len(), 3); 154 assert!(achievements.keys().all(|k| !k.is_empty())); 155 156 let achievement = achievements.get("9"); 157 assert!(achievement.is_some()); 158 let achievement = achievement.unwrap(); 159 assert_eq!(achievement.id, 14); 160 assert_eq!(&achievement.description, "Achievement id 14 description"); 161 assert_eq!(achievement.displayOrder, 27); 162 assert_eq!(&achievement.name, "Achievement id 14"); 163 assert_eq!(achievement.points, 10); 164 assert!(!achievement.unlocked); 165 } 166 167 #[test] 168 fn update() 169 { 170 let dateTime = DateTime::parse_from_str( 171 "20251207T050532+0000", 172 "%Y%m%dT%H%M%S%z" 173 ) 174 .unwrap() 175 .naive_utc(); 176 177 let metadata = serde_json::from_str(EarnedJson); 178 assert!(metadata.is_ok()); 179 180 let mut achievement = Sc2Achievement::default(); 181 assert!(!achievement.unlocked); 182 assert_eq!(achievement.unlockedTimestamp, None); 183 184 achievement.update(metadata.unwrap()); 185 assert!(achievement.unlocked); 186 assert_eq!(achievement.unlockedTimestamp.unwrap().naive_utc(), dateTime); 187 } 188 }