1use reqwest::blocking::Response;
8use reqwest::header::CONTENT_TYPE;
9use std::collections::hash_map::RandomState;
10use std::collections::HashMap;
11
12#[derive(Clone, Debug)]
17pub struct Client {
18 api_key: String,
19 application_name: String,
20}
21
22impl Client {
23 #[doc(hidden)]
24 pub fn request(&self, target: &str) -> Result<Response, Box<dyn std::error::Error>> {
25 let url = format!("https://theorangealliance.org/api{}", target);
26 let client = reqwest::blocking::Client::new();
27 let resp = client
28 .get(&url[..])
29 .header("X-TOA-Key", &self.api_key)
30 .header("X-Application-Origin", &self.application_name)
31 .header(CONTENT_TYPE, "application/json")
32 .send()?;
33
34 Ok(resp)
35 }
36 #[doc(hidden)]
37 pub fn api_key(&self) -> &str {
38 &self.api_key[..]
39 }
40 #[doc(hidden)]
41 pub fn application_name(&self) -> &str {
42 &self.application_name[..]
43 }
44
45 pub fn new(api_key: &str) -> Client {
52 Client {
53 api_key: api_key.to_string(),
54 application_name: "rustoa".to_string(),
55 }
56 }
57
58 pub fn api_version(&self) -> String {
70 let resp = match self.request("/") {
71 Ok(resp) => resp,
72 Err(e) => {
73 panic!("Something went wrong: {}", e);
74 }
75 };
76
77 let map = match resp.json::<HashMap<String, String>>() {
78 Ok(m) => m,
79 Err(e) => panic!("Something went wrong: {}", e),
80 };
81
82 match map.get("version") {
83 Some(vers) => vers.to_string(),
84 None => panic!("Something went wrong with the API."),
85 }
86 }
87 pub fn team(&self, team_number: u32) -> Team {
94 Team::new(team_number, self.clone())
95 }
96}
97
98pub struct Team {
102 #[doc(hidden)]
103 pub client: Client,
104 pub team_number: u32,
105}
106
107impl Team {
108 #[doc(hidden)]
109 pub fn new(team_number: u32, client: Client) -> Team {
110 Team {
111 client,
114 team_number,
115 }
116 }
117 fn get_wlt(&self) -> HashMap<String, u32, RandomState> {
118 let resp = match self.client.request(&format!("/team/{}/wlt", self.team_number)[..]) {
119 Ok(r) => r,
120 Err(e) => panic!("Something went wrong: {}", e)
121 };
122
123 let map = match resp.json::<Vec<HashMap<String, u32>>>() {
124 Ok(m) => m[0].clone(),
125 Err(e) => panic!("Something went wrong: {}", e)
126 };
127
128 map
129 }
130 pub fn wins(&self) -> u32 {
136 let map = self.get_wlt();
137
138 match map.get("wins") {
139 Some(w) => w.clone(),
140 None => panic!("Something went wrong with the API."),
141 }
142 }
143 pub fn losses(&self) -> u32 {
149 let map = self.get_wlt();
150
151 match map.get("losses") {
152 Some(l) => l.clone(),
153 None => panic!("Something went wrong with the API."),
154 }
155 }
156 pub fn ties(&self) -> u32 {
162 let map = self.get_wlt();
163
164 match map.get("ties") {
165 Some(t) => t.clone(),
166 None => panic!("Something went wrong with the API."),
167 }
168 }
169
170 pub fn properties(&self) -> HashMap<String, String, RandomState> {
183 let resp = match self
184 .client
185 .request(&format!("/team/{}/", self.team_number)[..])
186 {
187 Ok(resp) => resp,
188 Err(e) => panic!("Something went wrong: {}", e),
189 };
190
191 let map: serde_json::Value = match serde_json::from_str(&*match resp.text() {
192 Ok(text) => text,
193 Err(e) => panic!("Something went wrong: {}", e),
194 }) {
195 Ok(m) => m,
196 Err(e) => panic!("Something went wrong: {}", e),
197 };
198
199 let item = match map.as_array() {
200 Some(n) => n,
201 None => panic!("Something went wrong"),
202 };
203
204 let value = item[0].clone();
205
206 let new = match value.as_object() {
207 Some(m) => m,
208 None => panic!("Something went wrong"),
209 };
210
211 let mut new_map: HashMap<String, String> = HashMap::new();
212
213 for x in new.iter() {
214 let key = x.0.clone();
215 let value = match x.1 {
216 serde_json::Value::String(s) => s.clone(),
217 serde_json::Value::Number(n) => match n.as_u64() {
218 Some(u) => u.to_string(),
219 None => panic!("Something went wrong"),
220 },
221 serde_json::Value::Null => "null".to_string(),
222 _ => panic!("Something went wrong"),
223 };
224 let key_orig = key.clone();
225 if key == "last_active".to_string() {
226 let season = Season::value_of(value.clone());
227 let season = format!("{}", season);
228 new_map.insert(key_orig, season);
229 continue;
230 }
231 new_map.insert(key, value);
232 }
233
234 new_map
235 }
236 fn get_season_data(
237 &self,
238 season: Season,
239 query: &str
240 ) -> Result<f64, Box<dyn std::error::Error>> {
241 let season = season.value();
242 let resp = self
243 .client
244 .request(&format!("/team/{}/results/{}", self.team_number, season)[..])?;
245 let map: serde_json::Value = serde_json::from_str(&*resp.text()?)?;
246
247 let arr = match map.as_array() {
248 Some(a) => a,
249 None => panic!("Something went wrong")
250 };
251 let query = query.to_string();
252 let mut i = 0 as f64;
253 for val in arr.iter() {
254 let val = val.clone();
255 let val = &val[&query];
256 let num = match val.as_f64() {
257 Some(n) => n,
258 None => panic!("Something went wrong")
259 };
260 i += num;
261 }
262 i = (i * 100.0).round() / 100.0;
263 Ok(i)
264 }
265
266 pub fn season_wins(&self, season: Season) -> f64 {
276 let data = match self.get_season_data(season, "wins") {
277 Ok(m) => m,
278 Err(e) => panic!("Something went wrong: {}", e),
279 };
280
281 data
282 }
283
284 pub fn season_losses(&self, season: Season) -> f64 {
294 let data = match self.get_season_data(season, "losses") {
295 Ok(m) => m,
296 Err(e) => panic!("Something went wrong: {}", e),
297 };
298
299 data
300 }
301
302 pub fn season_ties(&self, season: Season) -> f64 {
312 let data = match self.get_season_data(season, "ties") {
313 Ok(m) => m,
314 Err(e) => panic!("Something went wrong: {}", e),
315 };
316
317 data
318 }
319
320 pub fn opr(&self, season: Season) -> f64 {
335 let data = match self.get_season_data(season, "opr") {
336 Ok(m) => m,
337 Err(e) => panic!("Something went wrong: {}", e),
338 };
339
340 data
341 }
342
343 pub fn np_opr(&self, season: Season) -> f64 {
353 let data = match self.get_season_data(season, "np_opr") {
354 Ok(m) => m,
355 Err(e) => panic!("Something went wrong: {}", e),
356 };
357
358 data
359 }
360
361 pub fn ranking_points(&self, season: Season) -> f64 {
375 let data = match self.get_season_data(season, "ranking_points") {
376 Ok(m) => m,
377 Err(e) => panic!("Something went wrong: {}", e),
378 };
379
380 data
381 }
382
383 pub fn qualifying_points(&self, season: Season) -> f64 {
395 let data = match self.get_season_data(season, "qualifying_points") {
396 Ok(m) => m,
397 Err(e) => panic!("Something went wrong: {}", e),
398 };
399
400 data
401 }
402
403 pub fn tiebreaker_points(&self, season: Season) -> f64 {
414 let data = match self.get_season_data(season, "tie_breaker_points") {
415 Ok(m) => m,
416 Err(e) => panic!("Something went wrong: {}", e),
417 };
418
419 data
420 }
421
422 pub fn events(&self, season: Season) -> HashMap<String, Event, RandomState> {
423 let resp = match self
424 .client
425 .request(&format!("/team/{}/events/{}", self.team_number, season.value())[..])
426 {
427 Ok(r) => match r.text() {
428 Ok(t) => t,
429 Err(e) => panic!("Something went wrong: {}", e),
430 },
431 Err(e) => panic!("Something went wrong: {}", e),
432 };
433 let json: serde_json::Value = match serde_json::from_str(&*resp) {
434 Ok(m) => m,
435 Err(e) => panic!("Something went wrong: {}", e),
436 };
437
438 let map = match json.as_array() {
439 Some(m) => m,
440 None => panic!("Something went wrong"),
441 };
442
443 let mut keys = Vec::new();
444
445 for val in map.iter() {
446 let key = match val["event_key"].as_str() {
447 Some(k) => k.to_string(),
448 None => panic!("Something went wrong"),
449 };
450 keys.push(key);
451 }
452
453 let mut emap: HashMap<String, Event> = HashMap::new();
454
455 for key in keys.iter() {
456 let event_key = key.clone();
457 let event = Event::new(&*key.clone(), &self.client);
458 let raw_key = event.name();
459 let mut key = raw_key.replace(" ", "_");
460 key = key.to_lowercase();
461 if emap.contains_key(&key[..]) {
462 let re = regex::Regex::new(r"\d{4}-\w+-").unwrap();
463 let raw_key_right = re.replace_all(&event_key[..], "");
464 key = format!("{}_{}", key, raw_key_right.to_lowercase());
465 }
466 emap.insert(key, event);
467 }
468
469 emap
470 }
471}
472
473#[derive(Clone, Debug)]
478pub struct Event {
479 pub event_key: String,
480 client: Client,
481}
482
483impl Event {
484 #[doc(hidden)]
485 pub fn new(event_key: &str, client: &Client) -> Event {
486 let event_key = event_key.to_string();
487 let client = client.clone();
488
489 Event { event_key, client }
490 }
491 #[doc(hidden)]
492 pub fn name(&self) -> String {
493 let resp = match self.client.request(&*format!("/event/{}", self.event_key)) {
494 Ok(r) => match r.text() {
495 Ok(t) => t,
496 Err(e) => panic!("Something went wrong: {}", e),
497 },
498 Err(e) => panic!("Something went wrong: {}", e),
499 };
500
501 let json: serde_json::Value = match serde_json::from_str(&resp[..]) {
502 Ok(v) => v,
503 Err(e) => panic!("Something went wrong: {}", e),
504 };
505
506 let val = match json.as_array() {
507 Some(v) => v[0].clone(),
508 None => panic!("Something went wrong"),
509 };
510 let val = &val["event_name"];
511 match &val.as_str() {
512 Some(s) => s.to_string(),
513 None => panic!("Something went wrong"),
514 }
515 }
516 pub fn properties(&self) -> HashMap<String, String, RandomState> {
529 let resp = match self
530 .client
531 .request(&format!("/event/{}", self.event_key)[..])
532 {
533 Ok(r) => match r.text() {
534 Ok(t) => t,
535 Err(e) => panic!("Something went wrong: {}", e),
536 },
537 Err(e) => panic!("Something went wrong: {}", e),
538 };
539
540 let json: serde_json::Value = match serde_json::from_str(&resp[..]) {
541 Ok(v) => v,
542 Err(e) => panic!("Something went wrong: {}", e),
543 };
544
545 let map = match json.as_array() {
546 Some(m) => m,
547 None => panic!("Something went wrong"),
548 };
549
550 let val = map[0].clone();
551
552 let new = match val.as_object() {
553 Some(n) => n,
554 None => panic!("Something went wrong"),
555 };
556
557 let mut new_map: HashMap<String, String> = HashMap::new();
558
559 for x in new.iter() {
560 let key = x.0.clone();
561 let value = match x.1 {
562 serde_json::Value::String(s) => s.clone(),
563 serde_json::Value::Number(n) => match n.as_u64() {
564 Some(u) => u.to_string(),
565 None => panic!("Something went wrong"),
566 },
567 serde_json::Value::Null => "null".to_string(),
568 serde_json::Value::Bool(b) => match b {
569 true => "true".to_string(),
570 false => "false".to_string(),
571 },
572 _ => panic!("Something went wrong"),
573 };
574 new_map.insert(key, value);
575 }
576
577 new_map
578 }
579 fn get_rankings_data(
580 &self,
581 team_number: u32,
582 query: &str,
583 ) -> Result<f64, Box<dyn std::error::Error>> {
584 let resp = self
585 .client
586 .request(&*format!("/event/{}/rankings", self.event_key))?;
587 let map: serde_json::Value = serde_json::from_str(&*resp.text()?)?;
588 let arr = match map.as_array() {
589 Some(a) => a,
590 None => panic!("Something went wrong"),
591 };
592 for val in arr.iter() {
593 let num = &val["team"]["team_number"];
594 let num = match num.as_f64() {
595 Some(n) => n as u32,
596 None => panic!("Something went wrong"),
597 };
598 if num == team_number {
599 match &val[query].as_f64() {
600 Some(n) => return Ok(n.clone()),
601 None => continue,
602 };
603 }
604 }
605 panic!("Something went wrong");
606 }
607
608 pub fn rank(&self, team_number: u32) -> f64 {
618 let resp = match self.get_rankings_data(team_number, "rank") {
619 Ok(o) => o,
620 Err(e) => panic!("Something went wrong: {}", e),
621 };
622 resp
623 }
624 pub fn rank_change(&self, team_number: u32) -> f64 {
634 let resp = match self.get_rankings_data(team_number, "rank_change") {
635 Ok(o) => o,
636 Err(e) => panic!("Something went wrong: {}", e),
637 };
638 resp
639 }
640 pub fn wins(&self, team_number: u32) -> f64 {
650 let resp = match self.get_rankings_data(team_number, "wins") {
651 Ok(o) => o,
652 Err(e) => panic!("Something went wrong: {}", e),
653 };
654 resp
655 }
656 pub fn losses(&self, team_number: u32) -> f64 {
666 let resp = match self.get_rankings_data(team_number, "losses") {
667 Ok(o) => o,
668 Err(e) => panic!("Something went wrong: {}", e),
669 };
670 resp
671 }
672 pub fn ties(&self, team_number: u32) -> f64 {
682 let resp = match self.get_rankings_data(team_number, "ties") {
683 Ok(o) => o,
684 Err(e) => panic!("Something went wrong: {}", e),
685 };
686 resp
687 }
688 pub fn opr(&self, team_number: u32) -> f64 {
698 let resp = match self.get_rankings_data(team_number, "opr") {
699 Ok(o) => o,
700 Err(e) => panic!("Something went wrong: {}", e),
701 };
702 resp
703 }
704 pub fn np_opr(&self, team_number: u32) -> f64 {
714 let resp = match self.get_rankings_data(team_number, "np_opr") {
715 Ok(o) => o,
716 Err(e) => panic!("Something went wrong: {}", e),
717 };
718 resp
719 }
720 pub fn highest_qualifier_score(&self, team_number: u32) -> f64 {
730 let resp = match self.get_rankings_data(team_number, "highest_qual_score") {
731 Ok(o) => o,
732 Err(e) => panic!("Something went wrong: {}", e),
733 };
734 resp
735 }
736 pub fn ranking_points(&self, team_number: u32) -> f64 {
746 let resp = match self.get_rankings_data(team_number, "ranking_points") {
747 Ok(o) => o,
748 Err(e) => panic!("Something went wrong: {}", e),
749 };
750 resp
751 }
752 pub fn qualifying_points(&self, team_number: u32) -> f64 {
762 let resp = match self.get_rankings_data(team_number, "qualifying_points") {
763 Ok(o) => o,
764 Err(e) => panic!("Something went wrong: {}", e),
765 };
766 resp
767 }
768 pub fn tiebreaker_points(&self, team_number: u32) -> f64 {
778 let resp = match self.get_rankings_data(team_number, "tie_breaker_points") {
779 Ok(o) => o,
780 Err(e) => panic!("Something went wrong: {}", e),
781 };
782 resp
783 }
784}
785
786pub enum Season {
798 SkyStone,
799 RoverRuckus,
800 RelicRecovery,
801 VelocityVortex,
802}
803
804impl Season {
805 #[doc(hidden)]
806 pub fn value(&self) -> i32 {
807 match self {
808 Season::SkyStone => 1920,
809 Season::RoverRuckus => 1819,
810 Season::RelicRecovery => 1718,
811 Season::VelocityVortex => 1617,
812 }
813 }
814 #[doc(hidden)]
815 pub fn value_of(s: String) -> Season {
816 match &s[..] {
817 "1920" => Season::SkyStone,
818 "1819" => Season::RoverRuckus,
819 "1718" => Season::RelicRecovery,
820 "1617" => Season::VelocityVortex,
821 _ => panic!("That season does not exist in the TOA database."),
822 }
823 }
824}
825
826impl std::fmt::Display for Season {
827 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
828 match self {
829 Season::SkyStone => write!(f, "Season::SkyStone"),
830 Season::RoverRuckus => write!(f, "Season::RoverRuckus"),
831 Season::RelicRecovery => write!(f, "Season::RelicRecovery"),
832 Season::VelocityVortex => write!(f, "Season::VelocityVortex"),
833 }
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 fn create_client() -> super::Client {
840 let key = match std::env::var("API_KEY") {
841 Ok(k) => k,
842 Err(e) => panic!("Something went wrong: {}", e),
843 };
844 super::Client::new(&*key)
845 }
846 #[test]
847 fn correct_version() {
848 let client = create_client();
849 assert_eq!("3.7.0", client.api_version());
850 }
851 #[test]
852 fn check_number() {
853 let client = create_client();
854 let team = client.team(16405);
855 assert_eq!(team.team_number, 16405);
856 }
857 #[test]
858 fn check_compat() {
859 let client = create_client();
860 let team1 = client.team(16405);
861 let team2 = client.team(16405);
862 assert_eq!(team1.wins(), team2.wins());
863 let year1 = match team1.properties().get("rookie_year") {
864 Some(y) => y.clone(),
865 None => panic!("Something went wrong"),
866 };
867 let year2 = match team2.properties().get("rookie_year") {
868 Some(y) => y.clone(),
869 None => panic!("Something went wrong"),
870 };
871 assert_eq!(year1, year2);
872 let event1 = match team1
873 .events(super::Season::SkyStone)
874 .get("trinity_river_qualifier")
875 {
876 Some(e) => e.clone(),
877 None => panic!("No value was found"),
878 };
879 let event2 = match team2
880 .events(super::Season::SkyStone)
881 .get("trinity_river_qualifier")
882 {
883 Some(e) => e.clone(),
884 None => panic!("No value was found"),
885 };
886 assert_eq!(event1.name(), event2.name());
887 assert_eq!(event1.opr(16405), event2.opr(16405));
888 }
889 #[test]
890 fn check_numbers() {
891 let client = create_client();
892 let team1 = client.team(16405);
893 let team2 = client.team(16405);
894 assert_eq!(team1.team_number, team2.team_number);
895 }
896 #[test]
897 fn test_property() {
898 let client = create_client();
899 let team = client.team(16405);
900 let year = match team.properties().get("rookie_year") {
901 Some(y) => y.clone(),
902 None => panic!("Something went wrong"),
903 };
904 assert_eq!("2019", year);
905 }
906
907 #[test]
908 fn test_season() {
909 let season = super::Season::SkyStone;
910 assert_eq!(season.value(), 1920);
911 }
912
913 #[test]
914 fn test_event() {
915 let client = create_client();
916 let team = client.team(16405);
917 let event = match team
918 .events(super::Season::SkyStone)
919 .get("trinity_river_qualifier")
920 {
921 Some(e) => e.clone(),
922 None => panic!("No value was found"),
923 };
924 let name1 = event.name();
925 let name2 = match event.properties().get("event_name") {
926 Some(n) => n.clone(),
927 None => panic!("Something went wrong"),
928 };
929 assert_eq!(name1, name2);
930 }
931}