1use core::{
2 any::{Any, TypeId, type_name},
3 fmt::{self, Debug, Formatter},
4 ptr,
5 ptr::DynMetadata,
6};
7
8#[cfg(feature = "alloc")]
9use alloc::{boxed::Box, rc::Rc, sync::Arc};
10
11pub trait TraitcastableTo<Target: 'static + ?Sized>: TraitcastableAny {
16 const METADATA: DynMetadata<Target>;
18}
19
20pub struct TraitcastTarget {
24 target_type_id: TypeId,
25 target_type_name: &'static str,
26 metadata: *const (),
28}
29impl TraitcastTarget {
30 #[must_use]
32 pub const fn from<Src: TraitcastableTo<Target>, Target: 'static + ?Sized>() -> Self {
33 #[allow(clippy::borrow_as_ptr)] Self {
35 target_type_id: TypeId::of::<Target>(),
36 target_type_name: type_name::<Target>(),
37 metadata: ptr::from_ref::<DynMetadata<Target>>(&Src::METADATA).cast::<()>(),
38 }
39 }
40 #[must_use]
42 pub const fn target_type_id(&self) -> TypeId {
43 self.target_type_id
44 }
45}
46
47pub unsafe trait TraitcastableAny: Any {
56 fn traitcast_targets(&self) -> &[TraitcastTarget];
64
65 fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
73 self
74 .traitcast_targets()
75 .iter()
76 .find(|possible| possible.target_type_id == target)
77 }
78
79 fn type_id(&self) -> TypeId {
81 Any::type_id(self)
82 }
83}
84
85pub trait TraitcastableAnyInfra<Target: ?Sized>: 'static {
89 fn is(&self) -> bool;
91
92 fn can_be(&self) -> bool;
94
95 fn downcast_ref(&self) -> Option<&Target>;
102
103 #[cfg(feature = "downcast_unchecked")]
107 #[doc(cfg(feature = "downcast_unchecked"))]
108 unsafe fn downcast_ref_unchecked(&self) -> &Target;
109
110 fn downcast_mut(&mut self) -> Option<&mut Target>;
117
118 #[cfg(feature = "downcast_unchecked")]
122 #[doc(cfg(feature = "downcast_unchecked"))]
123 unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target;
124}
125
126#[cfg(feature = "alloc")]
132#[doc(cfg(feature = "alloc"))]
133pub trait TraitcastableAnyInfraExt<Target: ?Sized + 'static>: Sized {
134 type Output;
136
137 fn downcast(self) -> Result<Self::Output, Self>;
145
146 #[cfg(feature = "downcast_unchecked")]
150 #[doc(cfg(feature = "downcast_unchecked"))]
151 unsafe fn downcast_unchecked(self) -> Self::Output;
152}
153
154#[cfg(feature = "min_specialization")]
155unsafe impl<T: 'static> TraitcastableAny for T {
159 default fn traitcast_targets(&self) -> &[TraitcastTarget] {
160 &[]
161 }
162 default fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
163 self
165 .traitcast_targets()
166 .iter()
167 .find(|possible| possible.target_type_id == target)
168 }
169}
170impl Debug for dyn TraitcastableAny {
171 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172 write!(f, "TraitcastableAny to {{")?;
173 for (i, target) in self.traitcast_targets().iter().enumerate() {
174 if i != 0 {
175 write!(f, ", ")?;
176 }
177 write!(f, "{}", target.target_type_name)?;
178 }
179 write!(f, "}}")
180 }
181}
182macro_rules! implement_with_markers {
183 ($($(+)? $traits:ident)*) => {
184 impl<Target: ?Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
185 default fn is(&self) -> bool {
186 false
187 }
188 default fn can_be(&self) -> bool {
189 let found_target = self.find_traitcast_target(TypeId::of::<Target>());
190 found_target.is_some()
191 }
192
193 default fn downcast_ref(&self) -> Option<&Target> {
194 let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
197
198 let raw_ptr = core::ptr::from_ref::<Self>(self).to_raw_parts().0;
199
200 metadata.map(|metadata| {
201 let ret_ptr: *const Target = ptr::from_raw_parts(raw_ptr, metadata);
202
203 unsafe {&*ret_ptr}
207 })
208 }
209 #[cfg(feature = "downcast_unchecked")]
210 default unsafe fn downcast_ref_unchecked(&self) -> &Target {
211 unsafe { self.downcast_ref().unwrap_unchecked() }
213 }
214
215 default fn downcast_mut(&mut self) -> Option<&mut Target> {
216 let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
219
220 let raw_ptr = core::ptr::from_mut::<Self>(self).to_raw_parts().0;
221
222 metadata.map(|metadata| {
223 let ret_ptr: *mut Target = ptr::from_raw_parts_mut(raw_ptr, metadata);
224
225 unsafe {&mut *ret_ptr}
229 })
230
231 }
232 #[cfg(feature = "downcast_unchecked")]
233 default unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
234 unsafe { self.downcast_mut().unwrap_unchecked() }
236 }
237 }
238 impl<Target: Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
239 fn is(&self) -> bool {
240 <dyn Any>::is::<Target>(self)
241 }
242 fn can_be(&self) -> bool {
243 <dyn TraitcastableAny as TraitcastableAnyInfra<Target>>::is(self)
244 }
245 fn downcast_ref(&self) -> Option<&Target> {
246 <dyn Any>::downcast_ref::<Target>(self)
247 }
248 #[cfg(feature = "downcast_unchecked")]
249 unsafe fn downcast_ref_unchecked(&self) -> &Target {
250 unsafe { <dyn Any>::downcast_ref_unchecked::<Target>(self) }
252 }
253
254 fn downcast_mut(&mut self) -> Option<&mut Target> {
255 <dyn Any>::downcast_mut::<Target>(self)
256 }
257 #[cfg(feature = "downcast_unchecked")]
258 unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
259 unsafe { <dyn Any>::downcast_mut_unchecked::<Target>(self) }
261 }
262 }
263 };
264}
265
266#[cfg(feature = "alloc")]
267impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
268 TraitcastableAnyInfraExt<Target> for Box<Src>
269{
270 type Output = Box<Target>;
271
272 default fn downcast(self) -> Result<Self::Output, Self> {
273 let raw = Self::into_raw(self);
274 if let Some(to_ref) = unsafe { &mut *raw }.downcast_mut() {
278 Ok(unsafe { Box::from_raw(to_ref) })
282 } else {
283 Err(unsafe { Self::from_raw(raw) })
286 }
287 }
288 #[cfg(feature = "downcast_unchecked")]
289 default unsafe fn downcast_unchecked(self) -> Self::Output {
290 unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
292 }
293}
294
295#[cfg(feature = "alloc")]
296impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
297 for Box<Src>
298{
299 fn downcast(self) -> Result<Self::Output, Self> {
300 #[cfg(feature = "downcast_unchecked")]
301 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
302 unsafe { Ok(<Box<dyn Any>>::downcast_unchecked(self)) }
305 } else {
306 Err(self)
307 }
308 #[cfg(not(feature = "downcast_unchecked"))]
309 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
310 Ok(<Box<dyn Any>>::downcast::<Target>(self).unwrap())
311 } else {
312 Err(self)
313 }
314 }
315
316 #[cfg(feature = "downcast_unchecked")]
317 unsafe fn downcast_unchecked(self) -> Self::Output {
318 unsafe { <Box<dyn Any>>::downcast_unchecked::<Target>(self) }
320 }
321}
322
323#[cfg(feature = "alloc")]
324impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
325 TraitcastableAnyInfraExt<Target> for Rc<Src>
326{
327 type Output = Rc<Target>;
328
329 default fn downcast(self) -> Result<Self::Output, Self> {
330 let raw = Self::into_raw(self);
331 if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
335 Ok(unsafe { Rc::from_raw(to_ref) })
339 } else {
340 Err(unsafe { Self::from_raw(raw) })
343 }
344 }
345 #[cfg(feature = "downcast_unchecked")]
346 default unsafe fn downcast_unchecked(self) -> Self::Output {
347 unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
349 }
350}
351
352#[cfg(feature = "alloc")]
353impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
354 for Rc<Src>
355{
356 fn downcast(self) -> Result<Self::Output, Self> {
357 #[cfg(feature = "downcast_unchecked")]
358 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
359 unsafe { Ok(<Rc<dyn Any>>::downcast_unchecked(self)) }
362 } else {
363 Err(self)
364 }
365 #[cfg(not(feature = "downcast_unchecked"))]
366 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
367 Ok(<Rc<dyn Any>>::downcast(self).unwrap())
368 } else {
369 Err(self)
370 }
371 }
372
373 #[cfg(feature = "downcast_unchecked")]
374 unsafe fn downcast_unchecked(self) -> Self::Output {
375 unsafe { <Rc<dyn Any>>::downcast_unchecked::<Target>(self) }
377 }
378}
379
380#[cfg(feature = "alloc")]
381impl<
382 Src: TraitcastableAnyInfra<Target> + ?Sized + Send + Sync,
383 Target: ?Sized + 'static + Send + Sync,
384> TraitcastableAnyInfraExt<Target> for Arc<Src>
385{
386 type Output = Arc<Target>;
387
388 default fn downcast(self) -> Result<Self::Output, Self> {
389 let raw = Self::into_raw(self);
390 if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
394 Ok(unsafe { Arc::from_raw(to_ref) })
398 } else {
399 Err(unsafe { Self::from_raw(raw) })
402 }
403 }
404 #[cfg(feature = "downcast_unchecked")]
405 default unsafe fn downcast_unchecked(self) -> Self::Output {
406 unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
408 }
409}
410
411#[cfg(feature = "alloc")]
412impl<Src: TraitcastableAnyInfra<Target> + Send + Sync, Target: Sized + 'static + Send + Sync>
413 TraitcastableAnyInfraExt<Target> for Arc<Src>
414{
415 fn downcast(self) -> Result<Self::Output, Self> {
416 #[cfg(feature = "downcast_unchecked")]
417 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
418 unsafe { Ok(<Arc<dyn Any + Send + Sync>>::downcast_unchecked(self)) }
421 } else {
422 Err(self)
423 }
424 #[cfg(not(feature = "downcast_unchecked"))]
425 if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
426 Ok(<Arc<dyn Any + Send + Sync>>::downcast(self).unwrap())
427 } else {
428 Err(self)
429 }
430 }
431
432 #[cfg(feature = "downcast_unchecked")]
433 unsafe fn downcast_unchecked(self) -> Self::Output {
434 unsafe { <Arc<dyn Any + Send + Sync>>::downcast_unchecked::<Target>(self) }
436 }
437}
438
439implement_with_markers!();
440implement_with_markers!(Send);
441implement_with_markers!(Send + Sync);