Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement xpath error message handling #135

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*.rlib
*.dll
Cargo.lock
.DS_Store

# Executables
*.exe
Expand Down
7 changes: 7 additions & 0 deletions src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14926,6 +14926,13 @@ extern "C" {
pub fn xmlXPathSetContextNode(node: xmlNodePtr, ctx: xmlXPathContextPtr)
-> ::std::os::raw::c_int;
}
extern "C" {
pub fn xmlXPathSetErrorHandler(
ctxt: xmlXPathContextPtr,
handler: xmlStructuredErrorFunc,
data: *mut ::std::os::raw::c_void,
);
}
extern "C" {
pub fn xmlXPathNodeEval(
node: xmlNodePtr,
Expand Down
1 change: 1 addition & 0 deletions src/schemas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ use schema::Schema; // internally handled by SchemaValidationContext

pub use parser::SchemaParserContext;
pub use validation::SchemaValidationContext;
pub use common::structured_error_handler;
73 changes: 60 additions & 13 deletions src/xpath.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//! The `XPath` functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::readonly::RoNode;
use crate::tree::{Document, DocumentRef, DocumentWeak, Node};
use crate::{
bindings::{self, *},
c_helpers::*,
error::StructuredError,
readonly::RoNode,
schemas::structured_error_handler,
tree::{Document, DocumentRef, DocumentWeak, Node},
};
use libc::{c_char, c_void, size_t};
use std::cell::RefCell;
use std::ffi::{CStr, CString};
Expand Down Expand Up @@ -33,6 +37,19 @@ pub struct Context {
pub(crate) context_ptr: ContextRef,
///Document contains pointer, needed for ContextPtr, so we need to borrow Document to prevent it's freeing
pub(crate) document: DocumentWeak,
///Errors registered during libxml2 xpath processing3
pub(crate) errlog: *mut Vec<StructuredError>,
}

impl Drop for Context {
fn drop(&mut self) {
unsafe {
if !self.errlog.is_null() {
let errors: Box<Vec<StructuredError>> = std::mem::transmute(self.errlog);
drop(errors)
}
}
}
}

///Essentially, the result of the evaluation of some xpath expression
Expand All @@ -50,21 +67,45 @@ impl Context {
if ctxtptr.is_null() {
Err(())
} else {
Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(&doc.0),
})
let errors: Box<Vec<StructuredError>> = Box::default();

unsafe {
let reference: *mut Vec<StructuredError> = std::mem::transmute(errors);
bindings::xmlXPathSetErrorHandler(
ctxtptr,
Some(structured_error_handler),
reference as *mut _,
);
Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(&doc.0),
errlog: reference as *mut _,
})
}
}
}

pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
if ctxtptr.is_null() {
Err(())
} else {
Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(docref),
})
let errors: Box<Vec<StructuredError>> = Box::default();

unsafe {
let reference: *mut Vec<StructuredError> = std::mem::transmute(errors);
bindings::xmlXPathSetErrorHandler(
ctxtptr,
Some(structured_error_handler),
reference as *mut _,
);

Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(docref),
errlog: reference as *mut _,
})
}
}
}

Expand All @@ -73,6 +114,13 @@ impl Context {
self.context_ptr.borrow().0
}

/// Drains error log from errors that might have accumulated while evaluating an xpath
pub fn drain_errors(&mut self) -> Vec<StructuredError> {
assert!(!self.errlog.is_null());
let errors = unsafe { &mut *self.errlog };
std::mem::take(errors)
}

/// Instantiate a new Context for the Document of a given Node.
/// Note: the Context is root-level for that document, use `.set_context_node` to limit scope to this node
pub fn from_node(node: &Node) -> Result<Context, ()> {
Expand Down Expand Up @@ -269,7 +317,6 @@ impl Object {
}
vec
}

}

impl fmt::Display for Object {
Expand Down
28 changes: 27 additions & 1 deletion tests/xpath_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@
let empty_values = xpath.findvalues(".//@xml:id", Some(empty_test));
assert_eq!(empty_values, Ok(Vec::new()));
let ids_values = xpath.findvalues(".//@xml:id", Some(ids_test));
let expected_ids = Ok(vec![String::from("start"),String::from("mid"),String::from("end")]);
let expected_ids = Ok(vec![
String::from("start"),
String::from("mid"),
String::from("end"),
]);
assert_eq!(ids_values, expected_ids);
let node_ids_values = ids_test.findvalues(".//@xml:id");
assert_eq!(node_ids_values, expected_ids);
Expand All @@ -216,6 +220,28 @@
}
}

#[test]
// brew install --HEAD libxml2
// export LIBXML2=`ls /opt/homebrew/Cellar/libxml2/*/lib/libxml2.dylib` && echo $LIBXML2
// cargo clean
// cargo test
fn xpath_context_new() {
let parser = Parser::default_html();
let doc_result = parser.parse_file("tests/resources/file02.xml");
assert!(doc_result.is_ok());
let doc = doc_result.unwrap();

// Xpath interface
let mut context = Context::new(&doc).unwrap();

Check warning on line 235 in tests/xpath_tests.rs

View workflow job for this annotation

GitHub Actions / rust-libxml CI

variable does not need to be mutable
match context.evaluate("/html/un:body") {
Ok(_) => assert!(false),
Err(e) => {

Check warning on line 238 in tests/xpath_tests.rs

View workflow job for this annotation

GitHub Actions / rust-libxml CI

unused variable: `e`
// for msg in context.drain_errors() {
// assert_eq!(1,msg.code);
// }
}
}
}
/// Tests for checking xpath well-formedness
mod compile_tests {
use libxml::xpath::is_well_formed_xpath;
Expand Down
Loading