1use core::ffi::CStr;
2use core::ptr::NonNull;
3
4pub trait PgNode: crate::seal::Sealed + Sized {
6 const CAST_TAGS: &'static [crate::NodeTag];
8
9 fn try_cast<T: PgNode>(node: &T) -> Option<&Self> {
11 if Self::CAST_TAGS.contains(&node.node_tag()) {
16 Some(unsafe { &*core::ptr::from_ref(node).cast() })
17 } else {
18 None
19 }
20 }
21
22 #[inline]
24 fn as_node(&self) -> &crate::Node {
25 unsafe { &*core::ptr::from_ref(self).cast() }
26 }
27
28 #[inline]
40 fn display_node(&self) -> String {
41 unsafe { display_node_impl(NonNull::from(self).cast()) }
44 }
45
46 #[doc(alias = "IsA")]
47 #[inline]
48 fn is_a(&self, tag: crate::NodeTag) -> bool {
49 self.node_tag() == tag
50 }
51
52 #[inline]
53 fn node_tag(&self) -> crate::NodeTag {
54 self.as_node().type_
55 }
56
57 #[inline]
59 fn try_as<T: PgNode>(&self) -> Option<&T> {
60 T::try_cast(self)
62 }
63}
64
65#[warn(unsafe_op_in_unsafe_fn)]
70pub(crate) unsafe fn display_node_impl(node: NonNull<crate::Node>) -> String {
71 unsafe {
76 let node_cstr = crate::nodeToString(node.as_ptr().cast());
77
78 let result = match CStr::from_ptr(node_cstr).to_str() {
79 Ok(cstr) => cstr.to_string(),
80 Err(e) => format!("<ffi error: {e:?}>"),
81 };
82
83 crate::pfree(node_cstr.cast());
84
85 result
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::PgNode;
92
93 #[test]
94 fn cast_roundtrip() {
95 let rt = crate::RangeTblRef { type_: crate::NodeTag::T_RangeTblRef, rtindex: 9 };
96
97 let node = rt.try_as::<crate::Node>().expect("upcast to Node should succeed");
98 let next = node.try_as::<crate::RangeTblRef>().expect("downcast to self should succeed");
99
100 assert_eq!(next.type_, crate::NodeTag::T_RangeTblRef);
101 assert_eq!(next.rtindex, 9);
102 }
103
104 #[test]
105 fn inheritance_downcast() {
106 let alt_subplan = crate::AlternativeSubPlan {
107 xpr: crate::Expr { type_: crate::NodeTag::T_AlternativeSubPlan },
108 subplans: std::ptr::null_mut(),
109 };
110
111 let node = alt_subplan.as_node(); let expr = node.try_as::<crate::Expr>().expect("downcast to parent of self should succeed");
113 assert_eq!(expr.node_tag(), crate::NodeTag::T_AlternativeSubPlan, "tag remains");
114
115 expr.try_as::<crate::AlternativeSubPlan>().expect("downcast to self should succeed");
116 assert!(node.try_as::<crate::Var>().is_none(), "downcast to an unrelated type should fail");
117 }
118
119 #[test]
120 fn inheritance_upcast() {
121 let alt_subplan = crate::AlternativeSubPlan {
122 xpr: crate::Expr { type_: crate::NodeTag::T_AlternativeSubPlan },
123 subplans: std::ptr::null_mut(),
124 };
125
126 let expr = alt_subplan.try_as::<crate::Expr>().expect("upcast to a parent should succeed");
127 assert_eq!(expr.node_tag(), crate::NodeTag::T_AlternativeSubPlan, "tag remains");
128
129 let node = expr.try_as::<crate::Node>().expect("upcast to Node should succeed");
130 assert_eq!(node.node_tag(), crate::NodeTag::T_AlternativeSubPlan, "tag remains");
131 }
132
133 #[cfg(any(feature = "pg13", feature = "pg14"))]
136 #[test]
137 fn value_struct_cast() {
138 let value =
139 crate::Value { type_: crate::NodeTag::T_Integer, val: crate::ValUnion { ival: 42 } };
140
141 let node = value.as_node(); assert_eq!(node.node_tag(), crate::NodeTag::T_Integer, "tag remains");
143
144 let next = node.try_as::<crate::Value>().expect("downcast to self should succeed");
145 assert_eq!(next.node_tag(), crate::NodeTag::T_Integer, "tag remains");
146 assert_eq!(unsafe { next.val.ival }, 42);
147
148 for tag in &[
149 crate::NodeTag::T_Float,
150 crate::NodeTag::T_String,
151 crate::NodeTag::T_BitString,
152 crate::NodeTag::T_Null,
153 ] {
154 let value = crate::Value {
155 type_: *tag,
156 val: crate::ValUnion { str_: c"something".as_ptr() as *mut _ },
157 };
158
159 let node = value.as_node(); assert_eq!(node.node_tag(), *tag, "tag remains");
161
162 let next = node.try_as::<crate::Value>().expect("downcast to self should succeed");
163 assert_eq!(next.node_tag(), *tag, "tag remains");
164 assert_eq!(unsafe { next.val.str_ }, c"something".as_ptr() as *mut _);
165 }
166 }
167
168 #[cfg(any(
172 feature = "pg15",
173 feature = "pg16",
174 feature = "pg17",
175 feature = "pg18",
176 feature = "pg19"
177 ))]
178 #[test]
179 fn value_union_cast() {
180 let vu = crate::ValUnion {
181 sval: crate::String {
182 type_: crate::NodeTag::T_String,
183 sval: c"something".as_ptr() as *mut _,
184 },
185 };
186
187 let node = vu.try_as::<crate::Node>().expect("upcast to Node should succeed");
188 let next = node.try_as::<crate::String>().expect("downcast to self should succeed");
189
190 assert_eq!(next.type_, crate::NodeTag::T_String);
191 assert_eq!(next.sval, c"something".as_ptr() as *mut _);
192
193 assert!(
194 node.try_as::<crate::ValUnion>().is_none(),
195 "downcast to untagged union should fail"
196 );
197 }
198}