1use std::fmt;
14use std::hash::{Hash, Hasher};
15use std::sync::{Arc, RwLock};
16
17use super::uoid::Uoid;
18
19pub struct KeyData {
21 pub uoid: Uoid,
22 pub start_pos: u32,
24 pub data_len: u32,
26 pub active_refs: u16,
28 }
31
32impl KeyData {
33 pub fn name(&self) -> &str {
34 &self.uoid.object_name
35 }
36
37 pub fn class_type(&self) -> u16 {
38 self.uoid.class_type
39 }
40}
41
42impl fmt::Debug for KeyData {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(
45 f,
46 "KeyData({:?}, pos={}, len={})",
47 self.uoid, self.start_pos, self.data_len
48 )
49 }
50}
51
52#[derive(Clone)]
57pub struct Key(Option<Arc<RwLock<KeyData>>>);
58
59impl Key {
60 pub fn null() -> Self {
62 Key(None)
63 }
64
65 pub fn from_uoid(uoid: Uoid) -> Self {
67 Key(Some(Arc::new(RwLock::new(KeyData {
68 uoid,
69 start_pos: 0,
70 data_len: 0,
71 active_refs: 0,
72 }))))
73 }
74
75 pub fn from_uoid_with_pos(uoid: Uoid, start_pos: u32, data_len: u32) -> Self {
77 Key(Some(Arc::new(RwLock::new(KeyData {
78 uoid,
79 start_pos,
80 data_len,
81 active_refs: 0,
82 }))))
83 }
84
85 pub fn is_null(&self) -> bool {
87 self.0.is_none()
88 }
89
90 pub fn is_some(&self) -> bool {
92 self.0.is_some()
93 }
94
95 pub fn inner(&self) -> Option<&Arc<RwLock<KeyData>>> {
97 self.0.as_ref()
98 }
99
100 pub fn uoid(&self) -> Uoid {
102 self.0
103 .as_ref()
104 .expect("uoid() called on null key")
105 .read()
106 .unwrap()
107 .uoid
108 .clone()
109 }
110
111 pub fn name(&self) -> String {
113 self.0
114 .as_ref()
115 .expect("name() called on null key")
116 .read()
117 .unwrap()
118 .uoid
119 .object_name
120 .clone()
121 }
122
123 pub fn class_type(&self) -> u16 {
125 self.0
126 .as_ref()
127 .expect("class_type() called on null key")
128 .read()
129 .unwrap()
130 .uoid
131 .class_type
132 }
133
134 pub fn try_name(&self) -> Option<String> {
136 self.0
137 .as_ref()
138 .map(|arc| arc.read().unwrap().uoid.object_name.clone())
139 }
140}
141
142impl Default for Key {
143 fn default() -> Self {
144 Key::null()
145 }
146}
147
148impl PartialEq for Key {
149 fn eq(&self, other: &Self) -> bool {
150 match (&self.0, &other.0) {
151 (None, None) => true,
152 (Some(a), Some(b)) => Arc::ptr_eq(a, b),
153 _ => false,
154 }
155 }
156}
157
158impl Eq for Key {}
159
160impl Hash for Key {
161 fn hash<H: Hasher>(&self, state: &mut H) {
162 match &self.0 {
163 None => 0u8.hash(state),
164 Some(arc) => {
165 1u8.hash(state);
166 Arc::as_ptr(arc).hash(state);
167 }
168 }
169 }
170}
171
172impl PartialOrd for Key {
173 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
174 Some(self.cmp(other))
175 }
176}
177
178impl Ord for Key {
179 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
180 match (&self.0, &other.0) {
181 (None, None) => std::cmp::Ordering::Equal,
182 (None, Some(_)) => std::cmp::Ordering::Less,
183 (Some(_), None) => std::cmp::Ordering::Greater,
184 (Some(a), Some(b)) => Arc::as_ptr(a).cmp(&Arc::as_ptr(b)),
185 }
186 }
187}
188
189impl fmt::Debug for Key {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 match &self.0 {
192 None => write!(f, "Key(nil)"),
193 Some(arc) => {
194 let data = arc.read().unwrap();
195 write!(f, "Key({:?})", data.uoid)
196 }
197 }
198 }
199}
200
201impl fmt::Display for Key {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match &self.0 {
204 None => write!(f, "(nil)"),
205 Some(arc) => {
206 let data = arc.read().unwrap();
207 write!(f, "{}", data.uoid)
208 }
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use super::super::location::Location;
217
218 #[test]
219 fn test_null_key() {
220 let k = Key::null();
221 assert!(k.is_null());
222 assert!(!k.is_some());
223 }
224
225 #[test]
226 fn test_key_from_uoid() {
227 let uoid = Uoid::new(Location::new(100, 0), 0x004C, "TestDrawable".into());
228 let k = Key::from_uoid(uoid);
229 assert!(!k.is_null());
230 assert_eq!(k.name(), "TestDrawable");
231 assert_eq!(k.class_type(), 0x004C);
232 }
233
234 #[test]
235 fn test_key_clone_shares_data() {
236 let uoid = Uoid::new(Location::new(100, 0), 0x0001, "Object".into());
237 let k1 = Key::from_uoid(uoid);
238 let k2 = k1.clone();
239 assert_eq!(k1, k2);
240 }
241
242 #[test]
243 fn test_key_with_pos() {
244 let uoid = Uoid::new(Location::new(100, 0), 0x0004, "Texture".into());
245 let k = Key::from_uoid_with_pos(uoid, 1000, 500);
246 let arc = k.inner().unwrap();
247 let data = arc.read().unwrap();
248 assert_eq!(data.start_pos, 1000);
249 assert_eq!(data.data_len, 500);
250 }
251
252 #[test]
253 fn test_null_keys_equal() {
254 assert_eq!(Key::null(), Key::null());
255 }
256
257 #[test]
258 fn test_different_keys_not_equal() {
259 let k1 = Key::from_uoid(Uoid::new(Location::new(1, 0), 1, "A".into()));
260 let k2 = Key::from_uoid(Uoid::new(Location::new(1, 0), 1, "A".into()));
261 assert_ne!(k1, k2);
263 }
264}