1use crate::player::PlayerId;
5use bon::Builder;
6use derive_more::From;
7use jiff::Zoned;
8use serde::{Deserialize, Serialize};
9use std::borrow::Cow;
10use std::collections::{HashMap, VecDeque};
11use std::num::NonZeroUsize;
12use std::sync::Arc;
13use strum::EnumIs;
14use uuid::Uuid;
15
16#[derive(Clone, Debug, Default, Deserialize, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct Chat {
19 public: ChatHistory,
20 private: HashMap<PlayerId, ChatHistory>,
21}
22
23impl Chat {
24 #[inline]
25 pub fn public(&self) -> ChatHistory {
26 self.public.clone()
27 }
28
29 #[inline]
30 pub fn private(&self, player: &PlayerId) -> ChatHistory {
31 self
32 .private
33 .get(player)
34 .cloned()
35 .unwrap_or_default()
36 }
37
38 pub(crate) fn push(&mut self, message: ChatMessage) {
39 self.public.push(message);
40 }
41}
42
43#[derive(Clone, Debug, Deserialize, Serialize)]
44#[serde(rename_all = "camelCase")]
45pub struct ChatHistory {
46 queue: VecDeque<ChatMessage>,
47 size: NonZeroUsize,
48}
49
50impl ChatHistory {
51 pub const MIN: NonZeroUsize = NonZeroUsize::new(100).unwrap();
52 pub const MAX: NonZeroUsize = NonZeroUsize::new(500).unwrap();
53
54 fn new(size: usize) -> Self {
55 let size = size.clamp(Self::MIN.get(), Self::MAX.get());
56 let size = unsafe { NonZeroUsize::new_unchecked(size) };
57 Self { queue: VecDeque::new(), size }
58 }
59
60 fn push(&mut self, message: ChatMessage) {
61 self.trim();
62 self.queue.push_back(message);
63 }
64
65 fn trim(&mut self) {
66 let size = self.size.get();
67 loop {
68 let len = self.queue.len();
69 if len.saturating_sub(1) >= size {
70 self.queue.pop_front();
71 } else {
72 break;
73 }
74 }
75 }
76}
77
78impl Default for ChatHistory {
79 fn default() -> Self {
80 Self::new(Self::MIN.get())
81 }
82}
83
84#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
85#[serde(rename_all = "camelCase")]
86pub struct ChatMessage {
87 #[builder(start_fn, into)]
88 content: ChatMessageContent,
89
90 #[builder(skip)]
91 id: ChatMessageId,
92
93 #[builder(default)]
94 kind: ChatMessageKind,
95
96 #[builder(default, into)]
97 author: ChatMessageAuthor,
98
99 #[builder(default = Zoned::now())]
100 time: Zoned,
101}
102
103impl ChatMessage {
104 #[inline]
105 pub fn id(&self) -> ChatMessageId {
106 self.id
107 }
108
109 #[inline]
110 pub fn kind(&self) -> ChatMessageKind {
111 self.kind
112 }
113}
114
115impl From<ChatMessage> for ChatMessageAuthor {
116 fn from(message: ChatMessage) -> Self {
117 message.author
118 }
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
122pub struct ChatMessageId(Uuid);
123
124impl ChatMessageId {
125 #[must_use]
126 pub fn new() -> Self {
127 Self(Uuid::now_v7())
128 }
129}
130
131impl Default for ChatMessageId {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137#[derive(Clone, Copy, Debug, Default, EnumIs, Deserialize, Serialize)]
138#[serde(rename_all = "kebab-case")]
139pub enum ChatMessageKind {
140 #[default]
141 Default,
142}
143
144#[derive(Clone, Debug, Default, Deserialize, Serialize)]
145#[serde(tag = "kind", rename_all = "kebab-case")]
146pub enum ChatMessageAuthor {
147 #[default]
148 System,
149 Player {
150 id: PlayerId,
151 },
152}
153
154impl From<PlayerId> for ChatMessageAuthor {
155 fn from(id: PlayerId) -> Self {
156 Self::Player { id }
157 }
158}
159
160impl From<&PlayerId> for ChatMessageAuthor {
161 fn from(id: &PlayerId) -> Self {
162 Self::Player { id: id.clone() }
163 }
164}
165
166#[derive(Debug, From, Deserialize, Serialize)]
167#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
168pub struct ChatMessageContent(Arc<str>);
169
170impl Clone for ChatMessageContent {
171 fn clone(&self) -> Self {
172 Self(Arc::clone(&self.0))
173 }
174}