1use crate::bindings::*;
4use crate::c_helpers::*;
5use crate::readonly::RoNode;
6use crate::tree::{Document, DocumentRef, DocumentWeak, Node};
7use libc::{c_char, c_void, size_t};
8use std::cell::RefCell;
9use std::ffi::{CStr, CString};
10use std::fmt;
11use std::rc::Rc;
12use std::str;
13
14pub(crate) type ContextRef = Rc<RefCell<_Context>>;
16
17#[derive(Debug)]
18pub(crate) struct _Context(pub(crate) xmlXPathContextPtr);
19
20impl Drop for _Context {
21 fn drop(&mut self) {
23 unsafe {
24 xmlXPathFreeContext(self.0);
25 }
26 }
27}
28
29#[derive(Clone)]
31pub struct Context {
32 pub(crate) context_ptr: ContextRef,
34 pub(crate) document: DocumentWeak,
36}
37
38#[derive(Debug)]
40pub struct Object {
41 pub ptr: xmlXPathObjectPtr,
43 document: DocumentWeak,
44}
45
46impl Context {
47 pub fn new(doc: &Document) -> Result<Context, ()> {
49 let ctxtptr = unsafe { xmlXPathNewContext(doc.doc_ptr()) };
50 if ctxtptr.is_null() {
51 Err(())
52 } else {
53 Ok(Context {
54 context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
55 document: Rc::downgrade(&doc.0),
56 })
57 }
58 }
59 pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
60 let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
61 if ctxtptr.is_null() {
62 Err(())
63 } else {
64 Ok(Context {
65 context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
66 document: Rc::downgrade(docref),
67 })
68 }
69 }
70
71 pub fn as_ptr(&self) -> xmlXPathContextPtr {
73 self.context_ptr.borrow().0
74 }
75
76 pub fn from_node(node: &Node) -> Result<Context, ()> {
79 let docref = node.get_docref().upgrade().unwrap();
80 Context::new_ptr(&docref)
81 }
82
83 pub fn register_namespace(&self, prefix: &str, href: &str) -> Result<(), ()> {
85 let c_prefix = CString::new(prefix).unwrap();
86 let c_href = CString::new(href).unwrap();
87 unsafe {
88 let result = xmlXPathRegisterNs(
89 self.as_ptr(),
90 c_prefix.as_bytes().as_ptr(),
91 c_href.as_bytes().as_ptr(),
92 );
93 if result != 0 {
94 Err(())
95 } else {
96 Ok(())
97 }
98 }
99 }
100
101 pub fn evaluate(&self, xpath: &str) -> Result<Object, ()> {
103 let c_xpath = CString::new(xpath).unwrap();
104 let ptr = unsafe { xmlXPathEvalExpression(c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
105 if ptr.is_null() {
106 Err(())
107 } else {
108 Ok(Object {
109 ptr,
110 document: self.document.clone(),
111 })
112 }
113 }
114
115 pub fn node_evaluate(&self, xpath: &str, node: &Node) -> Result<Object, ()> {
117 let c_xpath = CString::new(xpath).unwrap();
118 let ptr =
119 unsafe { xmlXPathNodeEval(node.node_ptr(), c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
120 if ptr.is_null() {
121 Err(())
122 } else {
123 Ok(Object {
124 ptr,
125 document: self.document.clone(),
126 })
127 }
128 }
129
130 pub fn node_evaluate_readonly(&self, xpath: &str, node: RoNode) -> Result<Object, ()> {
132 let c_xpath = CString::new(xpath).unwrap();
133 let ptr = unsafe { xmlXPathNodeEval(node.0, c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
134 if ptr.is_null() {
135 Err(())
136 } else {
137 Ok(Object {
138 ptr,
139 document: self.document.clone(),
140 })
141 }
142 }
143
144 pub fn set_context_node(&mut self, node: &Node) -> Result<(), ()> {
146 unsafe {
147 let result = xmlXPathSetContextNode(node.node_ptr(), self.as_ptr());
148 if result != 0 {
149 return Err(());
150 }
151 }
152 Ok(())
153 }
154
155 pub fn findnodes(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<Vec<Node>, ()> {
157 let evaluated = if let Some(node) = node_opt {
158 self.node_evaluate(xpath, node)?
159 } else {
160 self.evaluate(xpath)?
161 };
162 Ok(evaluated.get_nodes_as_vec())
163 }
164
165 pub fn findvalues(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<Vec<String>, ()> {
167 let evaluated = if let Some(node) = node_opt {
168 self.node_evaluate(xpath, node)?
169 } else {
170 self.evaluate(xpath)?
171 };
172 Ok(evaluated.get_nodes_as_str())
173 }
174
175 pub fn findvalue(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<String, ()> {
177 let evaluated = if let Some(node) = node_opt {
178 self.node_evaluate(xpath, node)?
179 } else {
180 self.evaluate(xpath)?
181 };
182 Ok(evaluated.to_string())
183 }
184}
185
186impl Drop for Object {
187 fn drop(&mut self) {
189 unsafe {
190 xmlXPathFreeObject(self.ptr);
191 }
192 }
193}
194
195impl Object {
196 pub fn get_number_of_nodes(&self) -> usize {
198 let v = xmlXPathObjectNumberOfNodes(self.ptr);
199 if v == -1 {
200 panic!("rust-libxml: xpath: Passed in null pointer!");
201 }
202 if v == -2 {
203 return 0;
205 }
206 if v < -2 {
207 panic!("rust-libxml: xpath: expected non-negative number of result nodes");
208 }
209 v as usize
210 }
211
212 pub fn get_nodes_as_vec(&self) -> Vec<Node> {
214 let n = self.get_number_of_nodes();
215 let mut vec: Vec<Node> = Vec::with_capacity(n);
216 let slice = if n > 0 {
217 xmlXPathObjectGetNodes(self.ptr, n as size_t)
218 } else {
219 Vec::new()
220 };
221 for ptr in slice {
222 if ptr.is_null() {
223 panic!("rust-libxml: xpath: found null pointer result set");
224 }
225 let node = Node::wrap(ptr, &self.document.upgrade().unwrap());
226 vec.push(node);
227 }
228 vec
229 }
230
231 pub fn get_readonly_nodes_as_vec(&self) -> Vec<RoNode> {
233 let n = self.get_number_of_nodes();
234 let mut vec: Vec<RoNode> = Vec::with_capacity(n);
235 let slice = if n > 0 {
236 xmlXPathObjectGetNodes(self.ptr, n as size_t)
237 } else {
238 Vec::new()
239 };
240 for ptr in slice {
241 if ptr.is_null() {
242 panic!("rust-libxml: xpath: found null pointer result set");
243 }
244 vec.push(RoNode(ptr));
245 }
246 vec
247 }
248
249 pub fn get_nodes_as_str(&self) -> Vec<String> {
251 let n = self.get_number_of_nodes();
252 let mut vec: Vec<String> = Vec::with_capacity(n);
253 let slice = if n > 0 {
254 xmlXPathObjectGetNodes(self.ptr, n as size_t)
255 } else {
256 Vec::new()
257 };
258 for ptr in slice {
259 if ptr.is_null() {
260 panic!("rust-libxml: xpath: found null pointer result set");
261 }
262 let value_ptr = unsafe { xmlXPathCastNodeToString(ptr) };
263 let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) };
264 let ready_str = c_value_string.to_string_lossy().into_owned();
265 bindgenFree(value_ptr as *mut c_void);
266 vec.push(ready_str);
267 }
268 vec
269 }
270
271}
272
273impl fmt::Display for Object {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 unsafe {
277 let receiver = xmlXPathCastToString(self.ptr);
278 let c_string = CStr::from_ptr(receiver as *const c_char);
279 let rust_string = str::from_utf8(c_string.to_bytes()).unwrap().to_owned();
280 bindgenFree(receiver as *mut c_void);
281 write!(f, "{rust_string}")
282 }
283 }
284}
285
286pub fn is_well_formed_xpath(xpath: &str) -> bool {
290 let c_xpath = CString::new(xpath).unwrap();
291 let xml_xpath_comp_expr_ptr = unsafe { xmlXPathCompile(c_xpath.as_bytes().as_ptr()) };
292 if xml_xpath_comp_expr_ptr.is_null() {
293 false
294 } else {
295 bindgenFree(xml_xpath_comp_expr_ptr as *mut c_void);
296 true
297 }
298}