1use crate::realtime::utils::{
2 serialize_all_subscription, serialize_char_ids_subscription, serialize_world_ids_subscription,
3};
4
5use crate::realtime::event::EventNames;
6use crate::realtime::Service;
7use crate::{CharacterID, WorldID};
8use serde::Serialize;
9use std::collections::HashSet;
10
11#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
12#[serde(untagged)]
13pub enum CharacterSubscription {
14 #[serde(serialize_with = "serialize_all_subscription")]
15 All,
16 #[serde(serialize_with = "serialize_char_ids_subscription")]
17 Ids(Vec<CharacterID>),
18}
19
20#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
21#[serde(untagged)]
22pub enum WorldSubscription {
23 #[serde(serialize_with = "serialize_all_subscription")]
24 All,
25 #[serde(serialize_with = "serialize_world_ids_subscription")]
27 Ids(Vec<WorldID>),
28}
29
30#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
31#[serde(untagged)]
32pub enum EventSubscription {
33 #[serde(serialize_with = "serialize_all_subscription")]
34 All,
35 Ids(Vec<EventNames>),
36}
37
38#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
39#[serde(rename_all = "camelCase")]
40pub struct SubscriptionSettings {
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub event_names: Option<EventSubscription>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub characters: Option<CharacterSubscription>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub logical_and_characters_with_worlds: Option<bool>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub worlds: Option<WorldSubscription>,
49 pub service: Service,
50}
51
52impl Default for SubscriptionSettings {
53 fn default() -> Self {
54 Self {
55 event_names: Some(EventSubscription::All),
56 characters: Some(CharacterSubscription::All),
57 logical_and_characters_with_worlds: None,
58 worlds: Some(WorldSubscription::All),
59 service: Service::Event,
60 }
61 }
62}
63
64impl SubscriptionSettings {
65 pub fn empty() -> Self {
66 Self {
67 event_names: None,
68 characters: None,
69 logical_and_characters_with_worlds: None,
70 worlds: None,
71 service: Service::Event,
72 }
73 }
74
75 pub fn is_empty(&self) -> bool {
76 self.event_names.is_none()
77 && self.characters.is_none()
78 && self.worlds.is_none()
79 && self.logical_and_characters_with_worlds.is_none()
80 }
81
82 pub fn merge(&mut self, other: Self) {
83 self.event_names = merge_event_subscription(self.event_names.take(), other.event_names);
84 self.characters = merge_character_subscription(self.characters.take(), other.characters);
85 self.worlds = merge_world_subscription(self.worlds.take(), other.worlds);
86 self.logical_and_characters_with_worlds = other
87 .logical_and_characters_with_worlds
88 .or(self.logical_and_characters_with_worlds);
89 self.service = other.service;
90 }
91
92 pub fn clear(&mut self, other: &Self) {
93 self.event_names =
94 clear_event_subscription(self.event_names.take(), other.event_names.as_ref());
95 self.characters =
96 clear_character_subscription(self.characters.take(), other.characters.as_ref());
97 self.worlds = clear_world_subscription(self.worlds.take(), other.worlds.as_ref());
98
99 if other.logical_and_characters_with_worlds.is_some() {
100 self.logical_and_characters_with_worlds = None;
101 }
102 }
103}
104
105fn merge_event_subscription(
106 current: Option<EventSubscription>,
107 update: Option<EventSubscription>,
108) -> Option<EventSubscription> {
109 match (current, update) {
110 (Some(EventSubscription::All), _) | (_, Some(EventSubscription::All)) => {
111 Some(EventSubscription::All)
112 }
113 (Some(EventSubscription::Ids(mut current)), Some(EventSubscription::Ids(update))) => {
114 current.extend(update);
115 current.dedup();
116 Some(EventSubscription::Ids(current))
117 }
118 (None, Some(update)) | (Some(update), None) => Some(update),
119 (None, None) => None,
120 }
121}
122
123fn merge_character_subscription(
124 current: Option<CharacterSubscription>,
125 update: Option<CharacterSubscription>,
126) -> Option<CharacterSubscription> {
127 match (current, update) {
128 (Some(CharacterSubscription::All), _) | (_, Some(CharacterSubscription::All)) => {
129 Some(CharacterSubscription::All)
130 }
131 (
132 Some(CharacterSubscription::Ids(mut current)),
133 Some(CharacterSubscription::Ids(update)),
134 ) => {
135 let mut seen = current.iter().copied().collect::<HashSet<_>>();
136 for id in update {
137 if seen.insert(id) {
138 current.push(id);
139 }
140 }
141 Some(CharacterSubscription::Ids(current))
142 }
143 (None, Some(update)) | (Some(update), None) => Some(update),
144 (None, None) => None,
145 }
146}
147
148fn merge_world_subscription(
149 current: Option<WorldSubscription>,
150 update: Option<WorldSubscription>,
151) -> Option<WorldSubscription> {
152 match (current, update) {
153 (Some(WorldSubscription::All), _) | (_, Some(WorldSubscription::All)) => {
154 Some(WorldSubscription::All)
155 }
156 (Some(WorldSubscription::Ids(mut current)), Some(WorldSubscription::Ids(update))) => {
157 let mut seen = current.iter().copied().collect::<HashSet<_>>();
158 for id in update {
159 if seen.insert(id) {
160 current.push(id);
161 }
162 }
163 Some(WorldSubscription::Ids(current))
164 }
165 (None, Some(update)) | (Some(update), None) => Some(update),
166 (None, None) => None,
167 }
168}
169
170fn clear_event_subscription(
171 current: Option<EventSubscription>,
172 clear: Option<&EventSubscription>,
173) -> Option<EventSubscription> {
174 match (current, clear) {
175 (None, _) => None,
176 (Some(_), Some(EventSubscription::All)) => None,
177 (Some(EventSubscription::All), Some(EventSubscription::Ids(_))) => {
178 Some(EventSubscription::All)
179 }
180 (Some(EventSubscription::Ids(current)), Some(EventSubscription::Ids(clear))) => {
181 let clear = clear.iter().cloned().collect::<HashSet<_>>();
182 let remaining = current
183 .into_iter()
184 .filter(|event| !clear.contains(event))
185 .collect::<Vec<_>>();
186
187 (!remaining.is_empty()).then_some(EventSubscription::Ids(remaining))
188 }
189 (some, None) => some,
190 }
191}
192
193fn clear_character_subscription(
194 current: Option<CharacterSubscription>,
195 clear: Option<&CharacterSubscription>,
196) -> Option<CharacterSubscription> {
197 match (current, clear) {
198 (None, _) => None,
199 (Some(_), Some(CharacterSubscription::All)) => None,
200 (Some(CharacterSubscription::All), Some(CharacterSubscription::Ids(_))) => {
201 Some(CharacterSubscription::All)
202 }
203 (Some(CharacterSubscription::Ids(current)), Some(CharacterSubscription::Ids(clear))) => {
204 let clear = clear.iter().copied().collect::<HashSet<_>>();
205 let remaining = current
206 .into_iter()
207 .filter(|id| !clear.contains(id))
208 .collect::<Vec<_>>();
209
210 (!remaining.is_empty()).then_some(CharacterSubscription::Ids(remaining))
211 }
212 (some, None) => some,
213 }
214}
215
216fn clear_world_subscription(
217 current: Option<WorldSubscription>,
218 clear: Option<&WorldSubscription>,
219) -> Option<WorldSubscription> {
220 match (current, clear) {
221 (None, _) => None,
222 (Some(_), Some(WorldSubscription::All)) => None,
223 (Some(WorldSubscription::All), Some(WorldSubscription::Ids(_))) => {
224 Some(WorldSubscription::All)
225 }
226 (Some(WorldSubscription::Ids(current)), Some(WorldSubscription::Ids(clear))) => {
227 let clear = clear.iter().copied().collect::<HashSet<_>>();
228 let remaining = current
229 .into_iter()
230 .filter(|id| !clear.contains(id))
231 .collect::<Vec<_>>();
232
233 (!remaining.is_empty()).then_some(WorldSubscription::Ids(remaining))
234 }
235 (some, None) => some,
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::{
242 CharacterSubscription, EventSubscription, SubscriptionSettings, WorldSubscription,
243 };
244 use crate::realtime::event::EventNames;
245 use crate::WorldID;
246
247 #[test]
248 fn merge_is_additive() {
249 let mut subscription = SubscriptionSettings::empty();
250 subscription.merge(SubscriptionSettings {
251 event_names: Some(EventSubscription::Ids(vec![EventNames::Death])),
252 ..SubscriptionSettings::empty()
253 });
254 subscription.merge(SubscriptionSettings {
255 worlds: Some(WorldSubscription::Ids(vec![WorldID::Emerald])),
256 ..SubscriptionSettings::empty()
257 });
258
259 assert_eq!(
260 subscription.event_names,
261 Some(EventSubscription::Ids(vec![EventNames::Death]))
262 );
263 assert_eq!(
264 subscription.worlds,
265 Some(WorldSubscription::Ids(vec![WorldID::Emerald]))
266 );
267 }
268
269 #[test]
270 fn clear_removes_requested_entries() {
271 let mut subscription = SubscriptionSettings {
272 event_names: Some(EventSubscription::Ids(vec![
273 EventNames::Death,
274 EventNames::PlayerLogin,
275 ])),
276 characters: Some(CharacterSubscription::Ids(vec![1, 2])),
277 worlds: Some(WorldSubscription::Ids(vec![WorldID::Emerald])),
278 logical_and_characters_with_worlds: Some(true),
279 ..SubscriptionSettings::empty()
280 };
281
282 subscription.clear(&SubscriptionSettings {
283 event_names: Some(EventSubscription::Ids(vec![EventNames::Death])),
284 characters: Some(CharacterSubscription::Ids(vec![2])),
285 logical_and_characters_with_worlds: Some(true),
286 ..SubscriptionSettings::empty()
287 });
288
289 assert_eq!(
290 subscription.event_names,
291 Some(EventSubscription::Ids(vec![EventNames::PlayerLogin]))
292 );
293 assert_eq!(
294 subscription.characters,
295 Some(CharacterSubscription::Ids(vec![1]))
296 );
297 assert_eq!(subscription.logical_and_characters_with_worlds, None);
298 }
299}