Skip to content
Open
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
8 changes: 7 additions & 1 deletion src/readonly.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
mod context;
mod document;
mod tree;
mod xpath;

pub use self::tree::RoNode;
pub use context::RoContext;
pub use document::RoDocument;
pub use tree::RoNode;
pub use xpath::RoObject;
67 changes: 67 additions & 0 deletions src/readonly/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::{
readonly::{RoDocument, RoNode, RoObject},
xpath::Context,
};

/// A read-only libxml2 Context
#[derive(Clone)]
pub struct RoContext(Context);

// SAFETY: we promise to only provide methods that need read-only access.
unsafe impl Sync for RoContext {}
unsafe impl Send for RoContext {}

impl RoContext {
/// create a read-only xpath context for a document
pub fn new(owner: &RoDocument) -> Result<Self, ()> {
let context = Context::new(&owner.0)?;
Ok(Self(context))
}

/// evaluate an xpath
pub fn evaluate(&self, xpath: &str) -> Result<RoObject, ()> {
self.0.evaluate(xpath).map(RoObject)
}

///evaluate an xpath on a context Node
pub fn node_evaluate(&self, xpath: &str, node: &RoNode) -> Result<RoObject, ()> {
self.0.node_evaluate_readonly(xpath, *node).map(RoObject)
}

/// evaluate an xpath on a context RoNode
pub fn node_evaluate_readonly(&self, xpath: &str, node: RoNode) -> Result<RoObject, ()> {
self.0.node_evaluate_readonly(xpath, node).map(RoObject)
}

/// find nodes via xpath, at a specified node or the document root
pub fn findnodes(&self, xpath: &str, node_opt: Option<&RoNode>) -> Result<Vec<RoNode>, ()> {
// Note: we cannot implemented this as `self.0.findnodes(...)` because that
// method takes `&mut self`.
let evaluated = if let Some(node) = node_opt {
self.node_evaluate(xpath, node)?
} else {
self.evaluate(xpath)?
};
Ok(evaluated.get_nodes_as_vec())
}

/// find literal values via xpath, at a specified node or the document root
pub fn findvalues(&self, xpath: &str, node_opt: Option<&RoNode>) -> Result<Vec<String>, ()> {
let evaluated = if let Some(node) = node_opt {
self.node_evaluate(xpath, node)?
} else {
self.evaluate(xpath)?
};
Ok(evaluated.get_nodes_as_str())
}

/// find a literal value via xpath, at a specified node or the document root
pub fn findvalue(&self, xpath: &str, node_opt: Option<&RoNode>) -> Result<String, ()> {
let evaluated = if let Some(node) = node_opt {
self.node_evaluate(xpath, node)?
} else {
self.evaluate(xpath)?
};
Ok(evaluated.to_string())
}
}
16 changes: 16 additions & 0 deletions src/readonly/document.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::{readonly::RoNode, tree::Document};

/// A read-only libxml2 Document
#[derive(Clone)]
pub struct RoDocument(pub(crate) Document);

// SAFETY: we promise to only provide methods that need read-only access.
unsafe impl Sync for RoDocument {}
unsafe impl Send for RoDocument {}

impl RoDocument {
/// Get the root element of the document
pub fn get_root_element(&self) -> Option<RoNode> {
self.0.get_root_readonly()
}
}
16 changes: 15 additions & 1 deletion src/readonly/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::str;

use crate::bindings::*;
use crate::c_helpers::*;
use crate::readonly::{RoContext, RoDocument};
use crate::tree::Document;
use crate::tree::namespace::Namespace;
use crate::tree::nodetype::NodeType;
use crate::tree::Document;
use crate::xpath::Context;

/// Lightweight struct for read-only parallel processing
Expand Down Expand Up @@ -518,6 +519,19 @@ impl RoNode {
Ok(evaluated.get_readonly_nodes_as_vec())
}

/// find read-only nodes via xpath, at the specified node and a given document
pub fn findnodes_readonly(self, xpath: &str, owner: &RoDocument) -> Result<Vec<RoNode>, ()> {
let context = RoContext::new(owner)?;
let evaluated = context.node_evaluate_readonly(xpath, self)?;
Ok(evaluated.get_nodes_as_vec())
}

/// find String values via xpath, at a specified node and a given document
pub fn findvalues_readonly(&self, xpath: &str, owner: &RoDocument) -> Result<Vec<String>, ()> {
let context = RoContext::new(owner)?;
context.findvalues(xpath, Some(self))
}

/// Read-only nodes are always linked
pub fn is_unlinked(self) -> bool {
false
Expand Down
35 changes: 35 additions & 0 deletions src/readonly/xpath.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::fmt;

use crate::{readonly::RoNode, xpath::Object};

/// Read-only version of the xpath object.
#[derive(Debug)]
pub struct RoObject(pub(crate) Object);

// SAFETY: we promise to only provide methods that need read-only access.
unsafe impl Sync for RoObject {}
unsafe impl Send for RoObject {}

impl RoObject {
/// returns the result set as a vector of `RoNode` objects
pub fn get_nodes_as_vec(&self) -> Vec<RoNode> {
self.0.get_readonly_nodes_as_vec()
}

/// returns the result set as a vector of Strings
pub fn get_nodes_as_str(&self) -> Vec<String> {
self.0.get_nodes_as_str()
}

/// get the number of nodes in the result set
pub fn get_number_of_nodes(&self) -> usize {
self.0.get_number_of_nodes()
}
}

impl fmt::Display for RoObject {
/// use if the XPath used was meant to return a string, such as string(//foo/@attr)
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
7 changes: 6 additions & 1 deletion src/tree/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::rc::{Rc, Weak};
use std::str;

use crate::bindings::*;
use crate::readonly::RoNode;
use crate::readonly::{RoDocument, RoNode};
use crate::tree::node::Node;

pub(crate) type DocumentRef = Rc<RefCell<_Document>>;
Expand Down Expand Up @@ -100,6 +100,11 @@ impl Document {
}
}

/// Create a readonly version of this document.
pub fn into_readonly(self) -> RoDocument {
RoDocument(self)
}

/// Obtain the underlying libxml2 `xmlDocPtr` for this Document
pub fn doc_ptr(&self) -> xmlDocPtr {
self.0.borrow().doc_ptr
Expand Down
Loading
Loading