Skip to main content

nil_core/report/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4pub mod battle;
5pub mod support;
6
7use crate::error::{Error, Result};
8use crate::player::PlayerId;
9use crate::round::RoundId;
10use battle::BattleReport;
11use itertools::Itertools;
12use jiff::Zoned;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::sync::Arc;
16use support::SupportReport;
17use uuid::Uuid;
18
19pub trait Report {
20  fn id(&self) -> ReportId;
21  fn round(&self) -> RoundId;
22  fn time(&self) -> &Zoned;
23}
24
25#[derive(Clone, Debug, Default, Deserialize, Serialize)]
26pub struct ReportManager {
27  reports: HashMap<ReportId, ReportKind>,
28  players: HashMap<PlayerId, Vec<ReportId>>,
29}
30
31impl ReportManager {
32  pub(crate) fn manage<I>(&mut self, report: ReportKind, players: I)
33  where
34    I: IntoIterator<Item = PlayerId>,
35  {
36    let id = report.as_dyn().id();
37    let players = players.into_iter().unique().collect_vec();
38
39    if !players.is_empty() {
40      for player in players {
41        self
42          .players
43          .entry(player)
44          .or_default()
45          .push(id);
46      }
47
48      debug_assert!(!self.reports.contains_key(&id));
49      self.reports.insert(id, report);
50    }
51  }
52
53  pub fn report(&self, id: ReportId) -> Result<&ReportKind> {
54    self
55      .reports
56      .get(&id)
57      .ok_or(Error::ReportNotFound(id))
58  }
59
60  pub fn reports_by<F>(&self, f: F) -> impl Iterator<Item = &ReportKind>
61  where
62    F: Fn((ReportId, &ReportKind)) -> bool,
63  {
64    self
65      .reports
66      .iter()
67      .filter(move |(id, report)| f((**id, report)))
68      .map(|(_, report)| report)
69  }
70
71  pub fn reports_of(&self, player: &PlayerId) -> impl Iterator<Item = ReportId> {
72    self
73      .players
74      .get(player)
75      .map(Vec::as_slice)
76      .unwrap_or_default()
77      .iter()
78      .copied()
79  }
80}
81
82#[derive(Clone, Debug, Deserialize, Serialize)]
83#[serde(tag = "kind", rename_all = "kebab-case")]
84#[remain::sorted]
85pub enum ReportKind {
86  Battle { report: Arc<BattleReport> },
87  Support { report: Arc<SupportReport> },
88}
89
90impl ReportKind {
91  pub fn as_dyn(&self) -> &dyn Report {
92    match self {
93      Self::Battle { report } => report.as_ref(),
94      Self::Support { report } => report.as_ref(),
95    }
96  }
97}
98
99#[must_use]
100#[derive(
101  Clone,
102  Copy,
103  Debug,
104  derive_more::Display,
105  PartialEq,
106  Eq,
107  PartialOrd,
108  Ord,
109  Hash,
110  Deserialize,
111  Serialize,
112)]
113pub struct ReportId(Uuid);
114
115impl ReportId {
116  #[inline]
117  pub fn new() -> Self {
118    Self(Uuid::now_v7())
119  }
120}
121
122impl Default for ReportId {
123  fn default() -> Self {
124    Self::new()
125  }
126}