skia_safe/modules/svg/
node_hierarchy.rs

1use crate::prelude::*;
2use std::{
3    fmt::{Debug, DebugStruct, Formatter, Result},
4    ops::{Deref, DerefMut},
5};
6
7use super::{Node, TypedNode};
8
9pub trait NodeSubtype {
10    type Base: NativeRefCounted;
11}
12
13/// All [`NodeSubType`] are defined to be a [`RcHandle<N>`] with `N` deriving from
14/// [`sb::SkRefCntBase`]
15impl<T: NodeSubtype> NativeRefCountedBase for T {
16    type Base = skia_bindings::SkRefCntBase;
17}
18
19impl<T: NativeRefCounted + NodeSubtype> RCHandle<T> {
20    pub fn as_base(&self) -> &RCHandle<T::Base> {
21        unsafe { transmute_ref(self) }
22    }
23
24    pub fn as_base_mut(&mut self) -> &mut RCHandle<T::Base> {
25        unsafe { transmute_ref_mut(self) }
26    }
27
28    /// All concrete node types can be converted to the supertype [`Node`].
29    pub fn into_node(self) -> Node {
30        unsafe { std::mem::transmute(self) }
31    }
32
33    /// All concrete node types can be converted to a [`TypedNode`]
34    pub fn typed(self) -> TypedNode {
35        self.into_node().typed()
36    }
37}
38
39impl<T: NativeRefCounted + NodeSubtype> Deref for RCHandle<T> {
40    type Target = RCHandle<T::Base>;
41
42    fn deref(&self) -> &Self::Target {
43        self.as_base()
44    }
45}
46
47/// This implementation of [`DerefMut`] causes subsequent UB when the containing
48/// [`RCHandle`] gets overwritten by a base type that does not match the actual
49/// underlying type.
50impl<T: NativeRefCounted + NodeSubtype> DerefMut for RCHandle<T> {
51    fn deref_mut(&mut self) -> &mut Self::Target {
52        self.as_base_mut()
53    }
54}
55
56impl<N: NativeRefCounted> Debug for RCHandle<N>
57where
58    Self: DebugAttributes,
59{
60    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
61        let mut builder = f.debug_struct(Self::NAME);
62
63        self._dbg(&mut builder);
64
65        builder.finish()
66    }
67}
68
69pub trait DebugAttributes {
70    const NAME: &'static str;
71
72    fn _dbg(&self, builder: &mut DebugStruct);
73}
74
75#[cfg(test)]
76mod tests {
77    use crate::svg::{Circle, Shape, TypedNode};
78
79    #[test]
80    fn subtype_can_access_supertype_attributes() {
81        let circle = Circle::default();
82        _ = circle.opacity();
83    }
84
85    #[test]
86    fn subtype_can_set_supertype_attributes() {
87        let mut circle = Circle::default();
88        circle.set_opacity(0.1);
89    }
90
91    #[test]
92    fn supertype_can_be_retrieved_and_contains_the_same_attributes() {
93        let mut circle = Circle::default();
94        circle.set_opacity(0.1);
95        let base: &Shape = circle.as_base();
96        assert_eq!(base.opacity(), Some(0.1));
97    }
98
99    #[test]
100    fn supertype_can_be_modified_and_affects_subtype() {
101        let mut circle = Circle::default();
102        let base: &mut Shape = circle.as_base_mut();
103        base.set_opacity(0.1);
104        assert_eq!(circle.opacity(), Some(0.1));
105    }
106
107    #[test]
108    fn concrete_node_can_be_converted_to_a_node() {
109        Circle::default().into_node();
110    }
111
112    #[test]
113    fn concrete_node_can_be_typed() {
114        let circle = Circle::default().typed();
115        assert!(matches!(circle, TypedNode::Circle(_)));
116    }
117}