use bitflags::bitflags;
use std::ffi::CString;
use std::os::unix::io::AsRawFd;
use std::slice;
use crate::context::Context;
use crate::error::{Error, Result};
use crate::iter::{
Ancestors, MetadataList, NodeIterable, Set, Siblings, Traverse,
};
use crate::schema::{SchemaModule, SchemaNode, SchemaNodeKind};
use crate::utils::*;
use libyang2_sys as ffi;
#[derive(Debug)]
pub struct DataTree<'a> {
context: &'a Context,
raw: *mut ffi::lyd_node,
}
#[derive(Clone, Debug)]
pub struct DataNodeRef<'a> {
tree: &'a DataTree<'a>,
raw: *mut ffi::lyd_node,
}
#[derive(Clone, Debug)]
pub struct Metadata<'a> {
dnode: &'a DataNodeRef<'a>,
raw: *mut ffi::lyd_meta,
}
#[derive(Debug)]
pub struct DataDiff<'a> {
tree: DataTree<'a>,
}
#[derive(Clone, Debug)]
pub enum DataDiffOp {
Create,
Delete,
Replace,
}
#[repr(u32)]
pub enum DataFormat {
XML = ffi::LYD_FORMAT::LYD_XML,
JSON = ffi::LYD_FORMAT::LYD_JSON,
}
bitflags! {
pub struct DataParserFlags: u32 {
const NO_VALIDATION = ffi::LYD_PARSE_ONLY;
const STRICT = ffi::LYD_PARSE_STRICT;
const NO_STATE = ffi::LYD_PARSE_NO_STATE;
}
}
bitflags! {
pub struct DataValidationFlags: u32 {
const NO_STATE = ffi::LYD_VALIDATE_NO_STATE;
const PRESENT = ffi::LYD_VALIDATE_PRESENT;
}
}
bitflags! {
pub struct DataPrinterFlags: u32 {
const WITH_SIBLINGS = ffi::LYD_PRINT_WITHSIBLINGS;
const SHRINK = ffi::LYD_PRINT_SHRINK;
const KEEP_EMPTY_CONT = ffi::LYD_PRINT_KEEPEMPTYCONT;
const WD_EXPLICIT = ffi::LYD_PRINT_WD_EXPLICIT;
const WD_TRIM = ffi::LYD_PRINT_WD_TRIM;
const WD_ALL = ffi::LYD_PRINT_WD_ALL;
}
}
pub trait Data {
#[doc(hidden)]
fn tree(&self) -> &DataTree;
#[doc(hidden)]
fn context(&self) -> &Context {
&self.tree().context
}
#[doc(hidden)]
fn raw(&self) -> *mut ffi::lyd_node {
self.tree().raw
}
fn find(&self, xpath: &str) -> Result<Set<DataNodeRef>> {
let xpath = CString::new(xpath).unwrap();
let mut set = std::ptr::null_mut();
let set_ptr = &mut set;
let ret =
unsafe { ffi::lyd_find_xpath(self.raw(), xpath.as_ptr(), set_ptr) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
let rnodes_count = unsafe { (*set).count } as usize;
let slice = if rnodes_count == 0 {
&[]
} else {
let rnodes = unsafe { (*set).__bindgen_anon_1.dnodes };
unsafe { slice::from_raw_parts(rnodes, rnodes_count) }
};
Ok(Set::new(self.tree(), slice))
}
fn find_single(&self, xpath: &str) -> Result<DataNodeRef> {
let mut dnodes = self.find(xpath)?;
let dnode = dnodes.next();
match dnode {
Some(_) if dnodes.next().is_some() => Err(Error {
errcode: ffi::LY_ERR::LY_ENOTFOUND,
msg: Some("Path refers to more than one data node".to_string()),
path: Some(xpath.to_string()),
apptag: None,
}),
Some(dnode) => Ok(dnode),
None => Err(Error {
errcode: ffi::LY_ERR::LY_ENOTFOUND,
msg: Some("Data node not found".to_string()),
path: Some(xpath.to_string()),
apptag: None,
}),
}
}
fn print_file<F: AsRawFd>(
&self,
fd: F,
format: DataFormat,
options: DataPrinterFlags,
) -> Result<()> {
let ret = unsafe {
ffi::lyd_print_fd(
fd.as_raw_fd(),
self.raw(),
format as u32,
options.bits(),
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(())
}
fn print_string(
&self,
format: DataFormat,
options: DataPrinterFlags,
) -> Result<String> {
let mut cstr = std::ptr::null_mut();
let cstr_ptr = &mut cstr;
let ret = unsafe {
ffi::lyd_print_mem(
cstr_ptr,
self.raw(),
format as u32,
options.bits(),
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(char_ptr_to_string(cstr))
}
}
impl<'a> DataTree<'a> {
fn reference(&self) -> DataNodeRef {
DataNodeRef {
tree: &self,
raw: self.raw,
}
}
pub fn new(context: &Context) -> Result<DataTree> {
let mut dtree = DataTree::from_raw(&context, std::ptr::null_mut());
dtree.validate(DataValidationFlags::empty())?;
Ok(dtree)
}
pub fn parse_file<F: AsRawFd>(
context: &Context,
fd: F,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_parse_data_fd(
context.raw,
fd.as_raw_fd(),
format as u32,
parser_options.bits(),
validation_options.bits(),
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(DataTree::from_raw(context, rnode))
}
pub fn parse_string(
context: &'a Context,
data: &str,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let data = CString::new(data).unwrap();
let ret = unsafe {
ffi::lyd_parse_data_mem(
context.raw,
data.as_ptr(),
format as u32,
parser_options.bits(),
validation_options.bits(),
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(DataTree::from_raw(context, rnode))
}
pub fn new_path(
&mut self,
xpath: &str,
value: Option<&str>,
) -> Result<Option<DataNodeRef>> {
let xpath = CString::new(xpath).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let value_cstr;
let value_ptr = match value {
Some(value) => {
value_cstr = CString::new(value).unwrap();
value_cstr.as_ptr()
}
None => std::ptr::null(),
};
let ret = unsafe {
ffi::lyd_new_path(
self.raw(),
self.context().raw,
xpath.as_ptr(),
value_ptr,
ffi::LYD_NEW_PATH_UPDATE,
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(DataNodeRef::from_raw_opt(self.tree(), rnode))
}
pub fn remove(&mut self, xpath: &str) -> Result<()> {
let dnode = self.find_single(xpath)?;
unsafe { ffi::lyd_free_tree(dnode.raw) };
Ok(())
}
pub fn validate(&mut self, options: DataValidationFlags) -> Result<()> {
let ret = unsafe {
ffi::lyd_validate_all(
&mut self.raw,
self.context.raw,
options.bits(),
std::ptr::null_mut(),
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.context));
}
Ok(())
}
pub fn duplicate(&self) -> Result<DataTree> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;
let options = ffi::LYD_DUP_RECURSIVE | ffi::LYD_DUP_WITH_FLAGS;
let ret = unsafe {
ffi::lyd_dup_siblings(
self.raw,
std::ptr::null_mut(),
options,
dup_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.context));
}
Ok(DataTree::from_raw(&self.context, dup))
}
pub fn merge(&mut self, source: &DataTree) -> Result<()> {
let options = 0u16;
let ret = unsafe {
ffi::lyd_merge_siblings(&mut self.raw, source.raw, options)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.context));
}
Ok(())
}
pub fn diff(&self, dtree: &'a DataTree) -> Result<DataDiff<'a>> {
let options = 0u16;
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_diff_siblings(self.raw, dtree.raw, options, rnode_ptr)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.context));
}
Ok(DataDiff {
tree: DataTree::from_raw(&dtree.context, rnode),
})
}
pub fn diff_apply(&mut self, diff: &DataDiff) -> Result<()> {
let ret =
unsafe { ffi::lyd_diff_apply_all(&mut self.raw, diff.tree.raw) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.context));
}
Ok(())
}
pub fn traverse(&'a self) -> impl Iterator<Item = DataNodeRef<'a>> {
let top = Siblings::new(Some(self.reference()));
top.flat_map(|dnode| dnode.traverse())
}
}
impl<'a> Data for DataTree<'a> {
fn tree(&self) -> &DataTree {
&self
}
}
impl<'a> Binding<'a> for DataTree<'a> {
type CType = ffi::lyd_node;
type Container = Context;
fn from_raw(context: &'a Context, raw: *mut ffi::lyd_node) -> DataTree {
DataTree { context, raw }
}
}
impl<'a> Drop for DataTree<'a> {
fn drop(&mut self) {
unsafe { ffi::lyd_free_all(self.raw) };
}
}
impl<'a> DataNodeRef<'a> {
pub fn schema(&self) -> SchemaNode {
let raw = unsafe { (*self.raw).schema };
SchemaNode::from_raw(self.context(), raw as *mut _)
}
pub fn owner_module(&self) -> SchemaModule {
let module = unsafe { ffi::lyd_owner_module(self.raw()) };
SchemaModule::from_raw(self.context(), module as *mut _)
}
pub fn ancestors(&self) -> Ancestors<'a, DataNodeRef<'a>> {
let parent = self.parent();
Ancestors::new(parent)
}
pub fn siblings(&self) -> Siblings<'a, DataNodeRef<'a>> {
let sibling = self.next_sibling();
Siblings::new(sibling)
}
pub fn children(&self) -> Siblings<'a, DataNodeRef<'a>> {
let child = self.first_child();
Siblings::new(child)
}
pub fn traverse(self) -> Traverse<'a, DataNodeRef<'a>> {
Traverse::new(self)
}
pub fn meta(&self) -> MetadataList {
let rmeta = unsafe { (*self.raw).meta };
let meta = Metadata::from_raw_opt(&self, rmeta);
MetadataList::new(meta)
}
pub fn path(&self) -> String {
let mut buf: [std::os::raw::c_char; 4096] = [0; 4096];
let pathtype = ffi::LYD_PATH_TYPE::LYD_PATH_LOG;
let ret = unsafe {
ffi::lyd_path(
self.raw,
pathtype,
buf.as_mut_ptr(),
buf.len() as u64,
)
};
if ret.is_null() {
panic!("Failed to generate path of the data node");
}
char_ptr_to_string(buf.as_ptr())
}
pub fn value(&self) -> Option<String> {
match self.schema().kind() {
SchemaNodeKind::Leaf | SchemaNodeKind::LeafList => {
let rnode = self.raw as *mut ffi::lyd_node_term;
let value = unsafe { (*rnode).value.canonical };
char_ptr_to_opt_string(value)
}
_ => None,
}
}
pub unsafe fn set_private(&mut self, ptr: *mut std::ffi::c_void) {
(*self.raw).priv_ = ptr;
}
pub fn get_private(&self) -> Option<*mut std::ffi::c_void> {
let priv_ = unsafe { (*self.raw).priv_ };
if priv_.is_null() {
None
} else {
Some(priv_)
}
}
}
impl<'a> Data for DataNodeRef<'a> {
fn tree(&self) -> &DataTree {
&self.tree
}
}
impl<'a> Binding<'a> for DataNodeRef<'a> {
type CType = ffi::lyd_node;
type Container = DataTree<'a>;
fn from_raw(
tree: &'a DataTree,
raw: *mut ffi::lyd_node,
) -> DataNodeRef<'a> {
DataNodeRef { tree, raw }
}
}
impl<'a> NodeIterable<'a> for DataNodeRef<'a> {
fn parent(&self) -> Option<DataNodeRef<'a>> {
let rparent = unsafe { ffi::lyd_parent(self.raw) };
DataNodeRef::from_raw_opt(&self.tree, rparent)
}
fn next_sibling(&self) -> Option<DataNodeRef<'a>> {
let rsibling = unsafe { (*self.raw).next };
DataNodeRef::from_raw_opt(&self.tree, rsibling)
}
fn first_child(&self) -> Option<DataNodeRef<'a>> {
let rchild = unsafe { ffi::lyd_child(self.raw) };
DataNodeRef::from_raw_opt(&self.tree, rchild)
}
}
impl<'a> PartialEq for DataNodeRef<'a> {
fn eq(&self, other: &DataNodeRef) -> bool {
self.raw == other.raw
}
}
impl<'a> Metadata<'a> {
pub fn name(&self) -> &str {
char_ptr_to_str(unsafe { (*self.raw).name })
}
pub fn value(&self) -> &str {
char_ptr_to_str(unsafe { (*self.raw).value.canonical })
}
#[doc(hidden)]
pub(crate) fn next(&self) -> Option<Metadata<'a>> {
let rnext = unsafe { (*self.raw).next };
Metadata::from_raw_opt(&self.dnode, rnext)
}
}
impl<'a> Binding<'a> for Metadata<'a> {
type CType = ffi::lyd_meta;
type Container = DataNodeRef<'a>;
fn from_raw(
dnode: &'a DataNodeRef,
raw: *mut ffi::lyd_meta,
) -> Metadata<'a> {
Metadata { dnode, raw }
}
}
impl<'a> PartialEq for Metadata<'a> {
fn eq(&self, other: &Metadata) -> bool {
self.raw == other.raw
}
}
impl<'a> DataDiff<'a> {
pub fn iter(&self) -> impl Iterator<Item = (DataDiffOp, DataNodeRef<'_>)> {
self.tree.traverse().filter_map(|dnode| {
match dnode.meta().find(|meta| meta.name() == "operation") {
Some(meta) => match meta.value() {
"create" => Some((DataDiffOp::Create, dnode)),
"delete" => Some((DataDiffOp::Delete, dnode)),
"replace" => Some((DataDiffOp::Replace, dnode)),
"none" => None,
_ => unreachable!(),
},
None => None,
}
})
}
pub fn reverse(&self) -> Result<DataDiff> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret =
unsafe { ffi::lyd_diff_reverse_all(self.tree.raw, rnode_ptr) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(&self.tree.context));
}
Ok(DataDiff {
tree: DataTree::from_raw(&self.tree.context, rnode),
})
}
}
impl<'a> Data for DataDiff<'a> {
fn tree(&self) -> &DataTree {
&self.tree
}
}