pickems/datatypes/
teams.rs1use std::collections::BTreeMap;
2use std::{fs::read_to_string, io::Write, path::PathBuf};
3
4use anyhow::anyhow;
5use serde::{Deserialize, Serialize};
6
7use crate::datatypes::{Index, Name, Rating, Seed, Set};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Team {
12 pub seed: Seed,
14 pub rating: Rating,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Map(BTreeMap<Name, Team>);
21
22impl Map {
23 pub fn parse_toml(filepath: PathBuf) -> anyhow::Result<Self> {
25 Ok(toml::from_str::<Self>(&read_to_string(filepath)?)?)
26 }
27
28 pub fn write_toml(&self, filepath: PathBuf) -> anyhow::Result<()> {
30 let contents = toml::to_string_pretty(self)?;
31
32 std::fs::OpenOptions::new()
33 .write(true)
34 .create(true)
35 .truncate(true)
36 .open(filepath)?
37 .write_all(contents.as_bytes())?;
38
39 Ok(())
40 }
41}
42
43impl<I: IntoIterator<Item = (Name, Team)>> From<I> for Map {
44 fn from(teams: I) -> Self {
45 Self(teams.into_iter().collect())
46 }
47}
48
49#[derive(Debug, Clone)]
53pub struct Teams {
54 pub names: [Name; 16],
56 pub ratings: [Rating; 16],
58}
59
60impl Teams {
61 #[must_use]
63 pub fn dummy() -> Self {
64 unsafe {
66 Self {
67 names: std::array::from_fn(|i| Name::new_unchecked(format!("Team {}", i + 1))),
68 ratings: std::array::from_fn(|i| Rating::new_unchecked(2000 - 50 * i as u16)),
69 }
70 }
71 }
72
73 pub fn parse_toml(filepath: PathBuf) -> anyhow::Result<Self> {
75 Self::try_from(Map::parse_toml(filepath)?)
76 }
77
78 pub fn write_toml(&self, filepath: PathBuf) -> anyhow::Result<()> {
80 let map = Map::from(self);
81 map.write_toml(filepath)
82 }
83}
84
85impl TryFrom<Map> for Teams {
86 type Error = anyhow::Error;
87
88 fn try_from(teams_map: Map) -> Result<Self, Self::Error> {
89 let set = teams_map
92 .0
93 .values()
94 .map(|team| Index::from(team.seed))
95 .collect::<Set>();
96
97 if set != Set::full() {
98 for i in Index::iter_all() {
99 if !set.contains(i) {
100 return Err(anyhow!("missing seed: {}", Seed::from(i)));
101 }
102 }
103
104 let indices = teams_map
107 .0
108 .values()
109 .map(|team| Index::from(team.seed))
110 .collect::<Vec<_>>();
111
112 for i in Index::iter_all() {
113 if indices.iter().filter(|&&index| index == i).count() > 1 {
114 return Err(anyhow!("duplicate seed: {}", Seed::from(i)));
115 }
116 }
117 }
118
119 if teams_map.0.len() != 16 {
120 return Err(anyhow!(
121 "there must be exactly 16 teams ({} teams recognised)",
122 teams_map.0.len(),
123 ));
124 }
125
126 let mut teams = teams_map.0.into_iter().collect::<Vec<_>>();
129 teams.sort_by_key(|(_, data)| data.seed);
130
131 let teams = teams
132 .into_iter()
133 .map(|(name, data)| (name, data.rating))
134 .collect::<Vec<_>>();
135
136 let ratings = teams
137 .iter()
138 .map(|(_, rating)| *rating)
139 .collect::<Vec<_>>()
140 .try_into()
141 .map_err(|_| anyhow!("failed to allocate array"))?;
142
143 let names = teams
144 .into_iter()
145 .map(|(name, _)| name)
146 .collect::<Vec<_>>()
147 .try_into()
148 .map_err(|_| anyhow!("failed to allocate array"))?;
149
150 Ok(Self { names, ratings })
151 }
152}
153
154impl From<&Teams> for Map {
155 fn from(teams: &Teams) -> Self {
156 Self(
157 (0..16)
158 .map(|i| {
159 (
160 teams.names[i].clone(),
161 Team {
162 seed: Seed::try_new(i as u16 + 1).unwrap(),
163 rating: teams.ratings[i],
164 },
165 )
166 })
167 .collect(),
168 )
169 }
170}