use std::collections::BTreeMap;
use std::{fs::read_to_string, io::Write, path::PathBuf};
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use crate::datatypes::{Index, Name, Rating, Seed, Set};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Team {
pub seed: Seed,
pub rating: Rating,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Map(BTreeMap<Name, Team>);
impl Map {
pub fn parse_toml(filepath: PathBuf) -> anyhow::Result<Self> {
Ok(toml::from_str::<Self>(&read_to_string(filepath)?)?)
}
pub fn write_toml(&self, filepath: PathBuf) -> anyhow::Result<()> {
let contents = toml::to_string_pretty(self)?;
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(filepath)?
.write_all(contents.as_bytes())?;
Ok(())
}
}
impl<I: IntoIterator<Item = (Name, Team)>> From<I> for Map {
fn from(teams: I) -> Self {
Self(teams.into_iter().collect())
}
}
#[derive(Debug, Clone)]
pub struct Teams {
pub names: [Name; 16],
pub ratings: [Rating; 16],
}
impl Teams {
#[must_use]
pub fn dummy() -> Self {
unsafe {
Self {
names: std::array::from_fn(|i| Name::new_unchecked(format!("Team {}", i + 1))),
ratings: std::array::from_fn(|i| Rating::new_unchecked(2000 - 50 * i as u16)),
}
}
}
pub fn parse_toml(filepath: PathBuf) -> anyhow::Result<Self> {
Self::try_from(Map::parse_toml(filepath)?)
}
pub fn write_toml(&self, filepath: PathBuf) -> anyhow::Result<()> {
let map = Map::from(self);
map.write_toml(filepath)
}
}
impl TryFrom<Map> for Teams {
type Error = anyhow::Error;
fn try_from(teams_map: Map) -> Result<Self, Self::Error> {
let set = teams_map
.0
.values()
.map(|team| Index::from(team.seed))
.collect::<Set>();
if set != Set::full() {
for i in Index::iter_all() {
if !set.contains(i) {
return Err(anyhow!("missing seed: {}", Seed::from(i)));
}
}
let indices = teams_map
.0
.values()
.map(|team| Index::from(team.seed))
.collect::<Vec<_>>();
for i in Index::iter_all() {
if indices.iter().filter(|&&index| index == i).count() > 1 {
return Err(anyhow!("duplicate seed: {}", Seed::from(i)));
}
}
}
if teams_map.0.len() != 16 {
return Err(anyhow!(
"there must be exactly 16 teams ({} teams recognised)",
teams_map.0.len(),
));
}
let mut teams = teams_map.0.into_iter().collect::<Vec<_>>();
teams.sort_by_key(|(_, data)| data.seed);
let teams = teams
.into_iter()
.map(|(name, data)| (name, data.rating))
.collect::<Vec<_>>();
let ratings = teams
.iter()
.map(|(_, rating)| *rating)
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anyhow!("failed to allocate array"))?;
let names = teams
.into_iter()
.map(|(name, _)| name)
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anyhow!("failed to allocate array"))?;
Ok(Self { names, ratings })
}
}
impl From<&Teams> for Map {
fn from(teams: &Teams) -> Self {
Self(
(0..16)
.map(|i| {
(
teams.names[i].clone(),
Team {
seed: Seed::try_new(i as u16 + 1).unwrap(),
rating: teams.ratings[i],
},
)
})
.collect(),
)
}
}