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> {
76 let old = canonical_room(old);
77 let new = canonical_room(new);
78 if old == new {
79 return Ok(());
80 }
81 let observed = self
82 .visibility
83 .remove(&old)
84 .ok_or_else(|| VisError::MissingRoom(old.clone()))?;
85 self.visibility.insert(new.clone(), observed);
86 for room_observed in self.visibility.values_mut() {
87 if room_observed.remove(&old) {
88 room_observed.insert(new.clone());
89 }
90 }
91 Ok(())
92 }
93
94 pub fn set_visible(
96 &mut self,
97 when_inside: &str,
98 show: &str,
99 visible: bool,
100 ) -> Result<(), VisError> {
101 let when_inside = canonical_room(when_inside);
102 let show = canonical_room(show);
103
104 if !self.visibility.contains_key(&when_inside) {
105 return Err(VisError::MissingRoom(when_inside));
106 }
107 if !self.visibility.contains_key(&show) {
108 return Err(VisError::MissingRoom(show));
109 }
110
111 if visible {
112 self.visibility
113 .entry(when_inside)
114 .or_default()
115 .insert(show.clone());
116 } else if let Some(observed) = self.visibility.get_mut(&when_inside) {
117 observed.remove(&show);
118 }
119 Ok(())
120 }
121
122 pub fn get_visible(&self, when_inside: &str, show: &str) -> Result<bool, VisError> {
124 let when_inside = canonical_room(when_inside);
125 let show = canonical_room(show);
126 let observed = self
127 .visibility
128 .get(&when_inside)
129 .ok_or_else(|| VisError::MissingRoom(when_inside.clone()))?;
130 if !self.visibility.contains_key(&show) {
131 return Err(VisError::MissingRoom(show));
132 }
133 Ok(observed.contains(&show))
134 }
135
136 pub fn set_all_visible(&mut self) {
138 let rooms: Vec<String> = self.visibility.keys().cloned().collect();
143 for observer in &rooms {
144 let observed = self
145 .visibility
146 .get_mut(observer)
147 .expect("observer was just collected from the map's keys");
148 observed.clear();
149 observed.extend(rooms.iter().filter(|r| *r != observer).cloned());
150 }
151 }
152}
153
154impl DecodeBinary for Vis {
155 type Error = VisError;
156
157 fn decode_binary(bytes: &[u8]) -> Result<Self, Self::Error> {
158 read_vis_from_bytes(bytes)
159 }
160}
161
162impl EncodeBinary for Vis {
163 type Error = VisError;
164
165 fn encode_binary(&self) -> Result<Vec<u8>, Self::Error> {
166 write_vis_to_vec(self)
167 }
168}
169
170#[derive(Debug, Error)]
172pub enum VisError {
173 #[error(transparent)]
175 Io(#[from] std::io::Error),
176 #[error("invalid VIS data: {0}")]
178 InvalidData(String),
179 #[error("VIS text encoding failed for {context}: {source}")]
181 TextEncoding {
182 context: String,
184 #[source]
186 source: EncodeTextError,
187 },
188 #[error("VIS text decoding failed for {context}: {source}")]
190 TextDecoding {
191 context: String,
193 #[source]
195 source: DecodeTextError,
196 },
197 #[error("VIS room `{0}` does not exist")]
199 MissingRoom(String),
200 #[error("invalid VIS room `{0}` (whitespace is not allowed)")]
202 InvalidRoom(String),
203}
204
205fn canonical_room(room: &str) -> String {
206 room.to_ascii_lowercase()
207}