1#![allow(dead_code)]
7use std::any::{Any, TypeId};
8use std::collections::HashMap;
9use std::fmt::{Debug, Formatter};
10use std::sync::{Arc, Mutex, MutexGuard, Once};
11
12use fake::{Dummy, Fake, Faker};
13
14#[derive(Clone, Eq, PartialEq)]
17pub struct Tagged<T> {
18 pub tag: String,
19 pub data: T,
20}
21
22impl<T: Debug> Debug for Tagged<T> {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("Tagged")
25 .field("tag", &self.tag)
26 .field("data", &self.data)
27 .finish()
28 }
29}
30
31type Lut = Option<Arc<Mutex<HashMap<TypeId, HashMap<String, Box<dyn Any>>>>>>;
32
33static INIT: Once = Once::new();
34static mut LUTS: Lut = None;
35
36type LutGuard = MutexGuard<'static, HashMap<TypeId, HashMap<String, Box<dyn Any>>>>;
37
38pub fn init() {
45 #[cfg(debug_assertions)]
46 INIT.call_once(|| {
47 unsafe {
48 LUTS = Some(Default::default());
49 };
50 });
51}
52
53fn lut() -> Option<LutGuard> {
54 unsafe {
55 #[allow(static_mut_refs)]
56 LUTS.as_ref()
57 }
58 .map(|luts| luts.lock().unwrap())
59}
60
61impl<T: Clone + 'static> Tagged<T> {
62 pub fn get<U: ToString, C: FnOnce() -> T>(_tag: U, _ctor: C) -> Option<Self> {
67 #[cfg(debug_assertions)]
68 {
69 let luts = lut();
70 luts.map(|mut luts| {
71 let lut = luts.entry(TypeId::of::<T>()).or_default();
72 let tag = _tag.to_string();
73 let data = lut
74 .entry(tag.clone())
75 .or_insert_with(|| Box::new(_ctor()))
76 .downcast_ref::<T>()
77 .unwrap()
78 .clone();
79 Self { tag, data }
80 })
81 }
82 #[cfg(not(debug_assertions))]
83 {
84 None
85 }
86 }
87}
88
89impl<T: Clone + Dummy<Faker> + 'static> Tagged<T> {
90 pub fn get_fake<U: ToString>(_tag: U) -> Option<Self> {
95 Self::get(_tag, || Faker.fake())
96 }
97}
98
99#[derive(Debug)]
100pub struct TypeNotFound;
101
102impl<T: Clone + PartialEq + 'static> Tagged<T> {
103 pub fn from_data(_data: &T) -> Result<Self, TypeNotFound> {
108 #[cfg(debug_assertions)]
109 {
110 let luts = lut().ok_or(TypeNotFound)?;
111 let lut = luts.get(&TypeId::of::<T>());
112
113 match lut {
114 Some(lut) => {
115 let tag = lut
116 .iter()
117 .find_map(|(k, v)| {
118 v.downcast_ref::<T>()
119 .and_then(|u| (u == _data).then_some(k.clone()))
120 })
121 .unwrap_or("value not found".into());
122
123 Ok(Self {
124 tag,
125 data: _data.clone(),
126 })
127 }
128 None => Err(TypeNotFound),
129 }
130 }
131 #[cfg(not(debug_assertions))]
132 Err(TypeNotFound)
133 }
134
135 pub fn tag(data: &T) -> Result<String, TypeNotFound> {
140 Self::from_data(data).map(|tagged| tagged.tag)
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use fake::Dummy;
147 use pathfinder_tagged_debug_derive::TaggedDebug;
148 use pretty_assertions_sorted::assert_eq;
149
150 use super::*;
151
152 #[derive(Clone, Copy, Default, Dummy, PartialEq, TaggedDebug)]
153 struct Unit;
154
155 #[derive(Clone, Copy, Default, Dummy, PartialEq, TaggedDebug)]
156 struct Tuple(i32, i32);
157
158 #[derive(Clone, Copy, Default, Dummy, PartialEq, TaggedDebug)]
159 struct Struct {
160 a: i32,
161 b: i32,
162 }
163
164 #[derive(Clone, Copy, Default, Dummy, PartialEq, TaggedDebug)]
165 enum Enum {
166 #[default]
167 A,
168 B(i32, i32),
169 C {
170 a: i32,
171 b: i32,
172 },
173 }
174
175 #[derive(Clone, Copy, Default, Dummy, PartialEq, TaggedDebug)]
176 struct Complex {
177 u: Unit,
178 t: Tuple,
179 e: Enum,
180 }
181
182 #[test]
183 fn lookup_and_debugs_work_correctly() {
184 let unit = Unit;
185 let tuple = Tuple(0, 1);
186 let stru = Struct { a: 0, b: 1 };
187 let enum_unit = Enum::A;
188 let enum_tuple = Enum::B(0, 1);
189 let enum_struct = Enum::C { a: 2, b: 3 };
190 let complex = Complex {
191 u: Unit,
192 t: Tuple(0, 1),
193 e: Enum::C { a: 2, b: 3 },
194 };
195
196 assert_eq!(format!("{:?}", unit), "Unit");
198 assert_eq!(format!("{:?}", tuple), "Tuple(0, 1)");
199 assert_eq!(format!("{:?}", stru), "Struct { a: 0, b: 1 }");
200 assert_eq!(format!("{:?}", enum_unit), "A");
201 assert_eq!(format!("{:?}", enum_tuple), "B(0, 1)");
202 assert_eq!(format!("{:?}", enum_struct), r#"C { a: 2, b: 3 }"#);
203 assert_eq!(
204 format!("{:?}", complex),
205 r#"Complex { u: Unit, t: Tuple(0, 1), e: C { a: 2, b: 3 } }"#
206 );
207
208 assert!(Tagged::<Unit>::get("unit", || unit).is_none());
210 assert!(Tagged::<Complex>::get_fake("complex").is_none());
211 assert!(Tagged::from_data(&complex).is_err());
212 assert!(Tagged::tag(&complex).is_err());
213
214 crate::init();
216
217 assert_eq!(format!("{:?}", unit), "Unit");
219 assert_eq!(format!("{:?}", tuple), "Tuple(0, 1)");
220 assert_eq!(format!("{:?}", stru), "Struct { a: 0, b: 1 }");
221 assert_eq!(format!("{:?}", enum_unit), "A");
222 assert_eq!(format!("{:?}", enum_tuple), "B(0, 1)");
223 assert_eq!(format!("{:?}", enum_struct), r#"C { a: 2, b: 3 }"#);
224 assert_eq!(
225 format!("{:?}", complex),
226 r#"Complex { u: Unit, t: Tuple(0, 1), e: C { a: 2, b: 3 } }"#
227 );
228
229 Tagged::<Unit>::get_fake("unit");
232 Tagged::<Tuple>::get("tuple", || tuple);
233 Tagged::<Struct>::get("struct", || stru);
234 Tagged::<Enum>::get("enum_unit", || enum_unit);
235 Tagged::<Enum>::get("enum_tuple", || enum_tuple);
236 Tagged::<Enum>::get("enum_struct", || enum_struct);
237 Tagged::<Complex>::get("complex", || complex);
238
239 assert_eq!(format!("{:?}", unit), r#"Unit { TAG: "unit" }"#);
240 assert_eq!(format!("{:?}", tuple), r#"Tuple("TAG: tuple", 0, 1)"#);
241 assert_eq!(
242 format!("{:?}", stru),
243 r#"Struct { TAG: "struct", a: 0, b: 1 }"#
244 );
245 assert_eq!(format!("{:?}", enum_unit), r#"A { TAG: "enum_unit" }"#);
246 assert_eq!(format!("{:?}", enum_tuple), r#"B("TAG: enum_tuple", 0, 1)"#);
247 assert_eq!(
248 format!("{:?}", enum_struct),
249 r#"C { TAG: "enum_struct", a: 2, b: 3 }"#
250 );
251 assert_eq!(
252 format!("{:?}", complex),
253 r#"Complex { TAG: "complex", u: Unit { TAG: "unit" }, t: Tuple("TAG: tuple", 0, 1), e: C { TAG: "enum_struct", a: 2, b: 3 } }"#
254 );
255 }
256}