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