1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::{Arc, Mutex, OnceLock};
4
5use image::{DynamicImage, GrayAlphaImage, GrayImage, ImageBuffer, Luma, LumaA, Rgb, RgbImage, Rgba, RgbaImage};
6
7type ConvertFn =
12 Box<dyn Fn(&Arc<dyn Any + Send + Sync>) -> Option<Box<dyn Any + Send + Sync>> + Send + Sync>;
13
14pub struct ConversionRegistry {
15 inner: HashMap<(TypeId, TypeId), ConvertFn>,
16 resolved_paths: HashMap<(TypeId, TypeId), Option<Vec<TypeId>>>,
17}
18
19impl ConversionRegistry {
20 pub fn new() -> Self {
21 Self {
22 inner: HashMap::new(),
23 resolved_paths: HashMap::new(),
24 }
25 }
26
27 pub fn register<S: Any + Send + Sync + 'static, T: Any + Send + Sync + 'static>(
28 &mut self,
29 f: fn(&S) -> Option<T>,
30 ) -> &mut Self {
31 let key = (TypeId::of::<S>(), TypeId::of::<T>());
32 self.inner.insert(
33 key,
34 Box::new(move |a| {
35 a.downcast_ref::<S>()
36 .and_then(|s| f(s).map(|t| Box::new(t) as Box<dyn Any + Send + Sync>))
37 }),
38 );
39 self.resolved_paths.clear();
40 self
41 }
42
43 fn convert_boxed(
44 &self,
45 a: &Arc<dyn Any + Send + Sync>,
46 to: TypeId,
47 ) -> Option<Box<dyn Any + Send + Sync>> {
48 let from = a.as_ref().type_id();
49 self.inner.get(&(from, to)).and_then(|f| f(a))
50 }
51
52 fn resolve_path(&mut self, from: TypeId, to: TypeId) -> Option<Vec<TypeId>> {
53 if let Some(cached) = self.resolved_paths.get(&(from, to)) {
54 return cached.clone();
55 }
56
57 if from == to {
58 self.resolved_paths.insert((from, to), Some(vec![from]));
59 return Some(vec![from]);
60 }
61
62 let mut queue: std::collections::VecDeque<TypeId> = std::collections::VecDeque::new();
64 let mut prev: HashMap<TypeId, TypeId> = HashMap::new();
65 queue.push_back(from);
66 prev.insert(from, from);
67
68 while let Some(cur) = queue.pop_front() {
69 if cur == to {
70 break;
71 }
72 for &(src, dst) in self.inner.keys() {
73 if src != cur {
74 continue;
75 }
76 if prev.contains_key(&dst) {
77 continue;
78 }
79 prev.insert(dst, cur);
80 queue.push_back(dst);
81 }
82 }
83
84 if !prev.contains_key(&to) {
85 self.resolved_paths.insert((from, to), None);
86 return None;
87 }
88
89 let mut path: Vec<TypeId> = Vec::new();
90 let mut cur = to;
91 loop {
92 path.push(cur);
93 let p = *prev.get(&cur).unwrap();
94 if p == cur {
95 break;
96 }
97 cur = p;
98 }
99 path.reverse();
100 self.resolved_paths.insert((from, to), Some(path.clone()));
101 Some(path)
102 }
103
104 pub fn convert_to<T: Any + Clone + Send + Sync + 'static>(
105 &mut self,
106 a: &Arc<dyn Any + Send + Sync>,
107 ) -> Option<T> {
108 let from = a.as_ref().type_id();
109 let to = TypeId::of::<T>();
110
111 if let Some(b) = self
113 .convert_boxed(a, to)
114 .and_then(|b| b.downcast::<T>().ok())
115 {
116 return Some((*b).clone());
117 }
118
119 let path = self.resolve_path(from, to)?;
121 if path.len() < 2 {
122 return None;
123 }
124
125 let mut cur: Arc<dyn Any + Send + Sync> = a.clone();
126 for win in path.windows(2) {
127 let from = win[0];
128 let to = win[1];
129 if cur.as_ref().type_id() != from {
130 return None;
131 }
132 let boxed = self.convert_boxed(&cur, to)?;
133 cur = Arc::from(boxed);
134 }
135
136 cur.downcast_ref::<T>().cloned()
137 }
138}
139
140impl Default for ConversionRegistry {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146fn default_registry() -> &'static Mutex<ConversionRegistry> {
148 static REG: OnceLock<Mutex<ConversionRegistry>> = OnceLock::new();
149 REG.get_or_init(|| {
150 let mut reg = ConversionRegistry::new();
151 reg.register::<i8, i16>(|v| Some(*v as i16))
153 .register::<i8, i32>(|v| Some(*v as i32))
154 .register::<i8, i64>(|v| Some(*v as i64))
155 .register::<i8, f64>(|v| Some(*v as f64))
156 .register::<i16, i32>(|v| Some(*v as i32))
157 .register::<i16, i64>(|v| Some(*v as i64))
158 .register::<i16, f64>(|v| Some(*v as f64))
159 .register::<i32, i64>(|v| Some(*v as i64))
160 .register::<i32, f64>(|v| Some(*v as f64))
161 .register::<i64, f64>(|v| Some(*v as f64))
162 .register::<u8, u16>(|v| Some(*v as u16))
163 .register::<u8, u32>(|v| Some(*v as u32))
164 .register::<u8, u64>(|v| Some(*v as u64))
165 .register::<u8, f64>(|v| Some(*v as f64))
166 .register::<u16, u32>(|v| Some(*v as u32))
167 .register::<u16, u64>(|v| Some(*v as u64))
168 .register::<u16, f64>(|v| Some(*v as f64))
169 .register::<u32, u64>(|v| Some(*v as u64))
170 .register::<u32, f64>(|v| Some(*v as f64))
171 .register::<f32, f64>(|v| Some(*v as f64))
172 .register::<bool, u8>(|v| Some(if *v { 1 } else { 0 }))
173 .register::<u8, bool>(|v| Some(*v != 0));
174
175 reg.register::<DynamicImage, DynamicImage>(|img| Some(img.clone()))
177 .register::<RgbaImage, DynamicImage>(|img| Some(DynamicImage::ImageRgba8(img.clone())))
178 .register::<RgbImage, DynamicImage>(|img| Some(DynamicImage::ImageRgb8(img.clone())))
179 .register::<GrayImage, DynamicImage>(|img| Some(DynamicImage::ImageLuma8(img.clone())))
180 .register::<GrayAlphaImage, DynamicImage>(|img| {
181 Some(DynamicImage::ImageLumaA8(img.clone()))
182 })
183 .register::<ImageBuffer<Rgba<u8>, Vec<u8>>, DynamicImage>(|img| {
184 Some(DynamicImage::ImageRgba8(img.clone()))
185 })
186 .register::<ImageBuffer<Rgb<u8>, Vec<u8>>, DynamicImage>(|img| {
187 Some(DynamicImage::ImageRgb8(img.clone()))
188 })
189 .register::<ImageBuffer<Luma<u8>, Vec<u8>>, DynamicImage>(|img| {
190 Some(DynamicImage::ImageLuma8(img.clone()))
191 })
192 .register::<ImageBuffer<LumaA<u8>, Vec<u8>>, DynamicImage>(|img| {
193 Some(DynamicImage::ImageLumaA8(img.clone()))
194 });
195
196 Mutex::new(reg)
197 })
198}
199
200pub fn convert_arc<T: Any + Clone + Send + Sync + 'static>(
202 a: &Arc<dyn Any + Send + Sync>,
203) -> Option<T> {
204 default_registry()
205 .lock()
206 .ok()
207 .and_then(|mut r| r.convert_to::<T>(a))
208}
209
210#[allow(dead_code)]
212pub fn register_conversion<S, T>(f: fn(&S) -> Option<T>)
213where
214 S: Any + Send + Sync + 'static,
215 T: Any + Send + Sync + 'static,
216{
217 if let Ok(mut reg) = default_registry().lock() {
218 reg.register::<S, T>(f);
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[derive(Clone, Debug, PartialEq, Eq)]
227 struct A(u8);
228 #[derive(Clone, Debug, PartialEq, Eq)]
229 struct B(u16);
230 #[derive(Clone, Debug, PartialEq, Eq)]
231 struct C(u32);
232
233 #[test]
234 fn chained_conversion_resolves() {
235 register_conversion::<A, B>(|a| Some(B(a.0 as u16)));
236 register_conversion::<B, C>(|b| Some(C(b.0 as u32)));
237
238 let a: Arc<dyn Any + Send + Sync> = Arc::new(A(7));
239 let out: Option<C> = convert_arc::<C>(&a);
240 assert_eq!(out, Some(C(7)));
241 }
242}