rakata_formats/vis/
mod.rs1mod reader;
20mod writer;
21
22pub use reader::{read_vis, read_vis_from_bytes};
23pub use writer::{write_vis, write_vis_to_vec};
24
25use std::collections::{BTreeMap, BTreeSet};
26use thiserror::Error;
27
28use rakata_core::{DecodeTextError, EncodeTextError};
29
30use crate::binary::{DecodeBinary, EncodeBinary};
31
32#[derive(Debug, Clone, PartialEq, Eq, Default)]
34pub struct Vis {
35 visibility: BTreeMap<String, BTreeSet<String>>,
36}
37
38impl Vis {
39 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn all_rooms(&self) -> BTreeSet<String> {
46 self.visibility.keys().cloned().collect()
47 }
48
49 pub fn visibility(&self) -> &BTreeMap<String, BTreeSet<String>> {
51 &self.visibility
52 }
53
54 pub fn room_exists(&self, room: &str) -> bool {
56 self.visibility.contains_key(&canonical_room(room))
57 }
58
59 pub fn add_room(&mut self, room: impl AsRef<str>) {
61 let room = canonical_room(room.as_ref());
62 self.visibility.entry(room).or_default();
63 }
64
65 pub fn remove_room(&mut self, room: &str) {
67 let room = canonical_room(room);
68 self.visibility.remove(&room);
69 for observed in self.visibility.values_mut() {
70 observed.remove(&room);
71 }
72 }
73
74 pub fn rename_room(&mut self, old: &str, new: &str) -> Result<(), VisError> {
82 let old = canonical_room(old);
83 let new = canonical_room(new);
84 if old == new {
85 return Ok(());
86 }
87 let observed = self
88 .visibility
89 .remove(&old)
90 .ok_or_else(|| VisError::MissingRoom(old.clone()))?;
91 self.visibility.insert(new.clone(), observed);
92 for room_observed in self.visibility.values_mut() {
93 if room_observed.remove(&old) {
94 room_observed.insert(new.clone());
95 }
96 }
97 Ok(())
98 }
99
100 pub fn set_visible(
108 &mut self,
109 when_inside: &str,
110 show: &str,
111 visible: bool,
112 ) -> Result<(), VisError> {
113 let when_inside = canonical_room(when_inside);
114 let show = canonical_room(show);
115
116 if !self.visibility.contains_key(&when_inside) {
117 return Err(VisError::MissingRoom(when_inside));
118 }
119 if !self.visibility.contains_key(&show) {
120 return Err(VisError::MissingRoom(show));
121 }
122
123 if visible {
124 self.visibility
125 .entry(when_inside)
126 .or_default()
127 .insert(show.clone());
128 } else if let Some(observed) = self.visibility.get_mut(&when_inside) {
129 observed.remove(&show);
130 }
131 Ok(())
132 }
133
134 pub fn get_visible(&self, when_inside: &str, show: &str) -> Result<bool, VisError> {
142 let when_inside = canonical_room(when_inside);
143 let show = canonical_room(show);
144 let observed = self
145 .visibility
146 .get(&when_inside)
147 .ok_or_else(|| VisError::MissingRoom(when_inside.clone()))?;
148 if !self.visibility.contains_key(&show) {
149 return Err(VisError::MissingRoom(show));
150 }
151 Ok(observed.contains(&show))
152 }
153
154 pub fn set_all_visible(&mut self) {
156 let rooms: Vec<String> = self.visibility.keys().cloned().collect();
161 for observer in &rooms {
162 let observed = self
163 .visibility
164 .get_mut(observer)
165 .expect("observer was just collected from the map's keys");
166 observed.clear();
167 observed.extend(rooms.iter().filter(|r| *r != observer).cloned());
168 }
169 }
170}
171
172impl DecodeBinary for Vis {
173 type Error = VisError;
174
175 fn decode_binary(bytes: &[u8]) -> Result<Self, Self::Error> {
176 read_vis_from_bytes(bytes)
177 }
178}
179
180impl EncodeBinary for Vis {
181 type Error = VisError;
182
183 fn encode_binary(&self) -> Result<Vec<u8>, Self::Error> {
184 write_vis_to_vec(self)
185 }
186}
187
188#[derive(Debug, Error)]
190pub enum VisError {
191 #[error(transparent)]
193 Io(#[from] std::io::Error),
194 #[error("invalid VIS data: {0}")]
196 InvalidData(String),
197 #[error("VIS text encoding failed for {context}: {source}")]
199 TextEncoding {
200 context: String,
202 #[source]
204 source: EncodeTextError,
205 },
206 #[error("VIS text decoding failed for {context}: {source}")]
208 TextDecoding {
209 context: String,
211 #[source]
213 source: DecodeTextError,
214 },
215 #[error("VIS room `{0}` does not exist")]
217 MissingRoom(String),
218 #[error("invalid VIS room `{0}` (whitespace is not allowed)")]
220 InvalidRoom(String),
221}
222
223fn canonical_room(room: &str) -> String {
224 room.to_ascii_lowercase()
225}