use bitflags::bitflags;
use core::ffi::{c_char, c_void};
use std::ffi::CStr;
use std::ffi::CString;
use std::mem::ManuallyDrop;
use std::slice;
use crate::context::Context;
use crate::error::{Error, Result};
use crate::iter::{
Ancestors, MetadataList, NodeIterable, Set, Siblings, Traverse,
};
use crate::schema::{DataValue, SchemaModule, SchemaNode, SchemaNodeKind};
use crate::utils::*;
use libyang5_sys as ffi;
#[derive(Debug)]
pub struct DataTree<'a> {
context: &'a Context,
raw: *mut ffi::lyd_node,
}
#[derive(Debug)]
pub struct DataTreeOwningRef<'a> {
pub tree: DataTree<'a>,
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, Copy, Debug, PartialEq)]
pub enum DataDiffOp {
Create,
Delete,
Replace,
}
#[allow(clippy::upper_case_acronyms)]
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DataFormat {
XML = ffi::LYD_FORMAT::LYD_XML,
JSON = ffi::LYD_FORMAT::LYD_JSON,
LYB = ffi::LYD_FORMAT::LYD_LYB,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DataOperation {
Data = ffi::lyd_type::LYD_TYPE_DATA_YANG,
RpcYang = ffi::lyd_type::LYD_TYPE_RPC_YANG,
NotificationYang = ffi::lyd_type::LYD_TYPE_NOTIF_YANG,
ReplyYang = ffi::lyd_type::LYD_TYPE_REPLY_YANG,
}
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;
const ANYDATA_STRICT = ffi::LYD_PARSE_ANYDATA_STRICT;
}
}
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_SIBLINGS;
const SHRINK = ffi::LYD_PRINT_SHRINK;
const KEEP_EMPTY_CONT = ffi::LYD_PRINT_EMPTY_CONT;
const WD_EXPLICIT = ffi::LYD_PRINT_WD_EXPLICIT;
const WD_TRIM = ffi::LYD_PRINT_WD_TRIM;
const WD_ALL = ffi::LYD_PRINT_WD_ALL;
}
}
bitflags! {
pub struct DataImplicitFlags: u32 {
const NO_STATE = ffi::LYD_IMPLICIT_NO_STATE;
const NO_CONFIG = ffi::LYD_IMPLICIT_NO_CONFIG;
const OUTPUT = ffi::LYD_IMPLICIT_OUTPUT;
const NO_DEFAULTS = ffi::LYD_IMPLICIT_NO_DEFAULTS;
}
}
bitflags! {
pub struct DataDiffFlags: u16 {
const DEFAULTS = ffi::LYD_DIFF_DEFAULTS as u16;
}
}
pub trait Data<'a> {
#[doc(hidden)]
fn context(&self) -> &'a Context {
self.tree().context
}
#[doc(hidden)]
fn tree(&self) -> &DataTree<'a>;
#[doc(hidden)]
fn raw(&self) -> *mut ffi::lyd_node;
fn find_xpath(&'a self, xpath: &str) -> Result<Set<'a, DataNodeRef<'a>>> {
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_path(&'a self, path: &str) -> Result<DataNodeRef<'a>> {
let path = CString::new(path).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_find_path(self.raw(), path.as_ptr(), 0u8, rnode_ptr)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(unsafe { DataNodeRef::from_raw(self.tree(), rnode as *mut _) })
}
fn find_output_path(&'a self, path: &str) -> Result<DataNodeRef<'a>> {
let path = CString::new(path).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_find_path(self.raw(), path.as_ptr(), 1u8, rnode_ptr)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(unsafe { DataNodeRef::from_raw(self.tree(), rnode as *mut _) })
}
#[cfg(not(target_os = "windows"))]
fn print_file<F: std::os::unix::io::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(())
}
#[cfg(target_os = "windows")]
fn print_file(
&self,
file: impl std::os::windows::io::AsRawHandle,
format: DataFormat,
options: DataPrinterFlags,
) -> Result<()> {
use libc::open_osfhandle;
let raw_handle = file.as_raw_handle();
let fd = unsafe { open_osfhandle(raw_handle as isize, 0) };
let ret = unsafe {
ffi::lyd_print_fd(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, true))
}
fn print_bytes(
&self,
format: DataFormat,
mut options: DataPrinterFlags,
) -> Result<Vec<u8>> {
let mut cstr = std::ptr::null_mut();
let cstr_ptr = &mut cstr;
let mut ly_out = std::ptr::null_mut();
let ret = unsafe { ffi::ly_out_new_memory(cstr_ptr, 0, &mut ly_out) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
let ret = unsafe {
if options.contains(DataPrinterFlags::WITH_SIBLINGS) {
options.remove(DataPrinterFlags::WITH_SIBLINGS);
ffi::lyd_print_all(
ly_out,
self.raw(),
format as u32,
options.bits(),
)
} else {
ffi::lyd_print_tree(
ly_out,
self.raw(),
format as u32,
options.bits(),
)
}
};
if ret != ffi::LY_ERR::LY_SUCCESS {
unsafe { ffi::ly_out_free(ly_out, None, 0) };
return Err(Error::new(self.context()));
}
let bytes = match format {
DataFormat::XML | DataFormat::JSON => {
let mut bytes =
unsafe { CStr::from_ptr(cstr) }.to_bytes().to_vec();
bytes.push(0);
bytes
}
DataFormat::LYB => {
let len = unsafe { ffi::ly_out_printed(ly_out) };
unsafe { std::slice::from_raw_parts(cstr as _, len as _) }
.to_vec()
}
};
unsafe { ffi::ly_out_free(ly_out, None, 0) };
Ok(bytes)
}
}
impl<'a> DataTree<'a> {
pub fn new(context: &'a Context) -> DataTree<'a> {
DataTree {
context,
raw: std::ptr::null_mut(),
}
}
pub fn into_raw(self) -> *mut ffi::lyd_node {
ManuallyDrop::new(self).raw
}
unsafe fn reroot(&mut self, raw: *mut ffi::lyd_node) {
if self.raw.is_null() {
let mut dnode = DataNodeRef::from_raw(self, raw);
while let Some(parent) = dnode.parent() {
dnode = parent;
}
self.raw = dnode.raw();
}
self.raw = ffi::lyd_first_sibling(self.raw);
}
#[cfg(not(target_os = "windows"))]
pub fn parse_file<F: std::os::unix::io::AsRawFd>(
context: &'a Context,
fd: F,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
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(unsafe { DataTree::from_raw(context, rnode) })
}
#[cfg(target_os = "windows")]
pub fn parse_file(
context: &'a Context,
file: impl std::os::windows::io::AsRawHandle,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
use libc::open_osfhandle;
let raw_handle = file.as_raw_handle();
let fd = unsafe { open_osfhandle(raw_handle as isize, 0) };
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_parse_data_fd(
context.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(unsafe { DataTree::from_raw(context, rnode) })
}
fn _parse_string(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let cdata;
let mut ly_in = std::ptr::null_mut();
let ret = match format {
DataFormat::XML | DataFormat::JSON => unsafe {
cdata = CString::new(data.as_ref()).unwrap();
ffi::ly_in_new_memory(cdata.as_ptr() as _, &mut ly_in)
},
DataFormat::LYB => unsafe {
ffi::ly_in_new_memory(data.as_ref().as_ptr() as _, &mut ly_in)
},
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
let ret = unsafe {
ffi::lyd_parse_data(
context.raw,
std::ptr::null_mut(),
ly_in,
format as u32,
parser_options.bits(),
validation_options.bits(),
rnode_ptr,
)
};
unsafe { ffi::ly_in_free(ly_in, 0) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(unsafe { DataTree::from_raw(context, rnode) })
}
pub fn parse_string(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
DataTree::_parse_string(
context,
data,
format,
parser_options,
validation_options,
)
}
fn _parse_op_string(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
op: DataOperation,
) -> Result<DataTree<'a>> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let cdata;
let mut ly_in = std::ptr::null_mut();
let ret = match format {
DataFormat::XML | DataFormat::JSON => unsafe {
cdata = CString::new(data.as_ref()).unwrap();
ffi::ly_in_new_memory(cdata.as_ptr() as _, &mut ly_in)
},
DataFormat::LYB => unsafe {
ffi::ly_in_new_memory(data.as_ref().as_ptr() as _, &mut ly_in)
},
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
let ret = unsafe {
ffi::lyd_parse_op(
context.raw,
std::ptr::null_mut(),
ly_in,
format as u32,
op as u32,
parser_options.bits(),
rnode_ptr,
std::ptr::null_mut(),
)
};
unsafe { ffi::ly_in_free(ly_in, 0) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(unsafe { DataTree::from_raw(context, rnode) })
}
pub fn parse_op_string(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
op: DataOperation,
) -> Result<DataTree<'a>> {
DataTree::_parse_op_string(context, data, format, parser_options, op)
}
pub fn reference(&self) -> Option<DataNodeRef<'_>> {
if self.raw.is_null() {
None
} else {
Some(DataNodeRef {
tree: self,
raw: self.raw,
})
}
}
pub fn new_path(
&mut self,
path: &str,
value: Option<&str>,
output: bool,
) -> Result<Option<DataNodeRef<'_>>> {
let path = CString::new(path).unwrap();
let mut rnode_root = std::ptr::null_mut();
let mut rnode = std::ptr::null_mut();
let rnode_root_ptr = &mut rnode_root;
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 mut options = ffi::LYD_NEW_PATH_UPDATE;
if output {
options |= ffi::LYD_NEW_VAL_OUTPUT;
}
eprintln!("Options {:b}", options);
let ret = unsafe {
ffi::lyd_new_path2(
self.raw(),
self.context().raw,
path.as_ptr(),
value_ptr as *const c_void,
0,
ffi::LYD_VALHINT_STRING,
options,
rnode_root_ptr,
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
if self.raw.is_null() {
self.raw = unsafe { ffi::lyd_first_sibling(rnode_root) };
} else {
self.raw = unsafe { ffi::lyd_first_sibling(self.raw) };
}
Ok(unsafe { DataNodeRef::from_raw_opt(self.tree(), rnode) })
}
pub fn remove(&mut self, path: &str) -> Result<()> {
let dnode = self.find_path(path)?;
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<'b>(&'b self) -> Result<DataTree<'a>> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;
if self.raw.is_null() {
return Ok(unsafe {
DataTree::from_raw(self.context, std::ptr::null_mut())
});
}
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(unsafe { DataTree::from_raw(self.context, dup) })
}
pub fn merge(&mut self, source: &DataTree<'_>) -> Result<()> {
if self.raw.is_null() {
let mut new_tree = source.duplicate()?;
self.raw = new_tree.raw;
new_tree.raw = std::ptr::null_mut();
} else {
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 add_implicit(&mut self, options: DataImplicitFlags) -> Result<()> {
let ret = unsafe {
ffi::lyd_new_implicit_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));
}
self.raw = unsafe { ffi::lyd_first_sibling(self.raw) };
Ok(())
}
pub fn diff(
&self,
dtree: &DataTree<'a>,
options: DataDiffFlags,
) -> Result<DataDiff<'a>> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_diff_siblings(
self.raw,
dtree.raw,
options.bits(),
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context));
}
Ok(DataDiff {
tree: unsafe { DataTree::from_raw(dtree.context, rnode) },
})
}
pub fn diff_apply(&mut self, diff: &DataDiff<'a>) -> 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(&self) -> impl Iterator<Item = DataNodeRef<'_>> {
let top = Siblings::new(self.reference());
top.flat_map(|dnode| dnode.traverse())
}
}
impl<'a> Data<'a> for DataTree<'a> {
fn tree(&self) -> &DataTree<'a> {
self
}
fn raw(&self) -> *mut ffi::lyd_node {
self.raw
}
}
unsafe impl<'a> Binding<'a> for DataTree<'a> {
type CType = ffi::lyd_node;
type Container = Context;
unsafe fn from_raw(
context: &'a Context,
raw: *mut ffi::lyd_node,
) -> DataTree<'a> {
DataTree { context, raw }
}
}
unsafe impl Send for DataTree<'_> {}
unsafe impl Sync for DataTree<'_> {}
impl Drop for DataTree<'_> {
fn drop(&mut self) {
unsafe { ffi::lyd_free_all(self.raw) };
}
}
impl<'a> DataTreeOwningRef<'a> {
unsafe fn from_raw(tree: DataTree<'a>, raw: *mut ffi::lyd_node) -> Self {
DataTreeOwningRef { tree, raw }
}
pub unsafe fn from_raw_node(
context: &Context,
raw: *mut ffi::lyd_node,
) -> std::mem::ManuallyDrop<DataTreeOwningRef<'_>> {
if (*(*(*raw).schema).module).ctx != context.raw {
panic!("raw node context differs from passed in context");
}
let mut tree = DataTree::new(context);
tree.reroot(raw);
std::mem::ManuallyDrop::new(DataTreeOwningRef { tree, raw })
}
pub fn new_path(
context: &'a Context,
path: &str,
value: Option<&str>,
output: bool,
) -> Result<Self> {
let mut tree = DataTree::new(context);
let raw = {
match tree.new_path(path, value, output)? {
Some(node) => node.raw,
None => tree.find_path(path)?.raw,
}
};
Ok(unsafe { DataTreeOwningRef::from_raw(tree, raw) })
}
pub fn noderef(&'a self) -> DataNodeRef<'a> {
DataNodeRef {
tree: &self.tree,
raw: self.raw,
}
}
fn _parse_op(
context: &'a Context,
raw: *mut ffi::lyd_node,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
op_type: ffi::lyd_type::Type,
op_node_ptr: *mut *mut ffi::lyd_node,
) -> Result<()> {
let mut opaque = std::ptr::null_mut();
let opaque_ptr = &mut opaque;
if format != DataFormat::JSON && format != DataFormat::XML {
return Err(Error {
errcode: ffi::LY_ERR::LY_EINVAL,
..Default::default()
});
}
let mut ly_in = std::ptr::null_mut();
let cdata = CString::new(data.as_ref()).unwrap();
let ret =
unsafe { ffi::ly_in_new_memory(cdata.as_ptr() as _, &mut ly_in) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
let ret = unsafe {
ffi::lyd_parse_op(
context.raw,
raw,
ly_in,
format as u32,
op_type,
parser_options.bits(),
opaque_ptr,
op_node_ptr,
)
};
unsafe { ffi::ly_in_free(ly_in, 0) };
unsafe { ffi::lyd_free_all(opaque) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(())
}
pub fn parse_netconf_rpc_op(
context: &'a Context,
data: impl AsRef<[u8]>,
parser_options: DataParserFlags,
) -> Result<DataTreeOwningRef<'a>> {
let mut tree = DataTreeOwningRef {
tree: DataTree::new(context),
raw: std::ptr::null_mut(),
};
Self::_parse_op(
context,
std::ptr::null_mut(),
data,
DataFormat::XML,
parser_options,
ffi::lyd_type::LYD_TYPE_RPC_NETCONF,
&mut tree.raw,
)?;
unsafe { tree.tree.reroot(tree.raw) };
Ok(tree)
}
pub fn parse_netconf_reply_op(
&mut self,
data: impl AsRef<[u8]>,
parser_options: DataParserFlags,
) -> Result<()> {
Self::_parse_op(
self.tree.context,
self.raw,
data,
DataFormat::XML,
parser_options,
ffi::lyd_type::LYD_TYPE_REPLY_NETCONF,
std::ptr::null_mut(),
)
}
pub fn parse_netconf_notif_op(
context: &'a Context,
data: impl AsRef<[u8]>,
parser_options: DataParserFlags,
) -> Result<DataTreeOwningRef<'a>> {
let mut tree = DataTreeOwningRef {
tree: DataTree::new(context),
raw: std::ptr::null_mut(),
};
Self::_parse_op(
context,
std::ptr::null_mut(),
data,
DataFormat::XML,
parser_options,
ffi::lyd_type::LYD_TYPE_NOTIF_NETCONF,
&mut tree.raw,
)?;
unsafe { tree.tree.reroot(tree.raw) };
Ok(tree)
}
pub fn parse_restconf_rpc_op(
&mut self,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
) -> Result<()> {
Self::_parse_op(
self.tree.context,
self.raw,
data,
format,
parser_options,
ffi::lyd_type::LYD_TYPE_RPC_RESTCONF,
std::ptr::null_mut(),
)
}
pub fn parse_restconf_reply_op(
&mut self,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
) -> Result<()> {
Self::_parse_op(
self.tree.context,
self.raw,
data,
format,
parser_options,
ffi::lyd_type::LYD_TYPE_REPLY_RESTCONF,
std::ptr::null_mut(),
)
}
pub fn parse_restconf_notif_op(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
) -> Result<DataTreeOwningRef<'a>> {
let mut tree = DataTreeOwningRef {
tree: DataTree::new(context),
raw: std::ptr::null_mut(),
};
if format == DataFormat::XML {
Self::_parse_op(
context,
std::ptr::null_mut(),
data,
DataFormat::XML,
parser_options,
ffi::lyd_type::LYD_TYPE_NOTIF_NETCONF,
&mut tree.raw,
)?;
} else {
Self::_parse_op(
context,
std::ptr::null_mut(),
data,
DataFormat::JSON,
parser_options,
ffi::lyd_type::LYD_TYPE_NOTIF_RESTCONF,
&mut tree.raw,
)?;
}
unsafe { tree.tree.reroot(tree.raw) };
Ok(tree)
}
}
impl<'a> From<DataTree<'a>> for DataTreeOwningRef<'a> {
fn from(tree: DataTree<'a>) -> DataTreeOwningRef<'a> {
let raw = tree.raw;
unsafe { DataTreeOwningRef::from_raw(tree, raw) }
}
}
impl<'a> From<&'a DataTreeOwningRef<'_>> for DataNodeRef<'a> {
fn from(tree: &'a DataTreeOwningRef<'_>) -> DataNodeRef<'a> {
DataNodeRef {
tree: &tree.tree,
raw: tree.raw,
}
}
}
impl<'a> Data<'a> for DataTreeOwningRef<'a> {
fn tree(&self) -> &DataTree<'a> {
&self.tree
}
fn raw(&self) -> *mut ffi::lyd_node {
self.raw
}
}
unsafe impl Send for DataTreeOwningRef<'_> {}
unsafe impl Sync for DataTreeOwningRef<'_> {}
impl<'a> DataNodeRef<'a> {
pub fn as_raw(&self) -> *mut ffi::lyd_node {
self.raw
}
pub fn schema(&self) -> SchemaNode<'_> {
let raw = unsafe { (*self.raw).schema };
unsafe { SchemaNode::from_raw(self.context(), raw as *mut _) }
}
pub fn owner_module(&self) -> SchemaModule<'_> {
let module = unsafe { ffi::lyd_owner_module(self.raw()) };
unsafe { 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 inclusive_ancestors(&self) -> Ancestors<'a, DataNodeRef<'a>> {
Ancestors::new(Some(self.clone()))
}
pub fn siblings(&self) -> Siblings<'a, DataNodeRef<'a>> {
let sibling = self.next_sibling();
Siblings::new(sibling)
}
pub fn inclusive_siblings(&self) -> Siblings<'a, DataNodeRef<'a>> {
Siblings::new(Some(self.clone()))
}
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.clone())
}
pub fn list_keys(&self) -> impl Iterator<Item = DataNodeRef<'a>> {
self.children().filter(|dnode| dnode.schema().is_list_key())
}
pub fn meta(&self) -> MetadataList<'_> {
let rmeta = unsafe { (*self.raw).meta };
let meta = unsafe { Metadata::from_raw_opt(self, rmeta) };
MetadataList::new(meta)
}
pub fn path(&self) -> String {
let mut buf: [c_char; 4096] = [0; 4096];
let pathtype = ffi::LYD_PATH_TYPE::LYD_PATH_STD;
let ret = unsafe {
ffi::lyd_path(self.raw, pathtype, buf.as_mut_ptr(), buf.len())
};
if ret.is_null() {
panic!("Failed to generate path of the data node");
}
char_ptr_to_string(buf.as_ptr(), false)
}
pub fn value_canonical(&self) -> Option<String> {
match self.schema().kind() {
SchemaNodeKind::Leaf | SchemaNodeKind::LeafList => {
let rnode = self.raw as *mut ffi::lyd_node_term;
let mut value = unsafe { (*rnode).value._canonical };
if value.is_null() {
value = unsafe {
ffi::lyd_value_get_canonical(
self.context().raw,
&(*rnode).value,
)
};
}
char_ptr_to_opt_string(value, false)
}
_ => None,
}
}
pub fn value(&self) -> Option<DataValue> {
match self.schema().kind() {
SchemaNodeKind::Leaf | SchemaNodeKind::LeafList => {
let rnode = self.raw as *const ffi::lyd_node_term;
let rvalue = unsafe { (*rnode).value };
let value =
unsafe { DataValue::from_raw(self.tree.context, &rvalue) };
Some(value)
}
_ => None,
}
}
pub fn is_default(&self) -> bool {
match self.schema().kind() {
SchemaNodeKind::Leaf | SchemaNodeKind::LeafList => {
(unsafe { ffi::lyd_is_default(self.raw) }) != 0
}
_ => false,
}
}
pub fn duplicate(&self, with_parents: bool) -> Result<DataTree<'a>> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;
if self.raw.is_null() {
return Ok(unsafe {
DataTree::from_raw(self.tree.context, std::ptr::null_mut())
});
}
let mut options = ffi::LYD_DUP_RECURSIVE | ffi::LYD_DUP_WITH_FLAGS;
if with_parents {
options |= ffi::LYD_DUP_WITH_PARENTS;
}
let ret = unsafe {
ffi::lyd_dup_single(
self.raw,
std::ptr::null_mut(),
options,
dup_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
if with_parents {
let mut dnode = unsafe { DataNodeRef::from_raw(self.tree, dup) };
while let Some(parent) = dnode.parent() {
dnode = parent;
}
dup = dnode.raw();
}
Ok(unsafe { DataTree::from_raw(self.tree.context, dup) })
}
pub unsafe fn set_private(&mut self, ptr: *mut c_void) {
(*self.raw).priv_ = ptr;
}
pub fn get_private(&self) -> Option<*mut c_void> {
let priv_ = unsafe { (*self.raw).priv_ };
if priv_.is_null() {
None
} else {
Some(priv_)
}
}
pub fn new_inner(
&mut self,
module: Option<&SchemaModule<'_>>,
name: &str,
) -> Result<DataNodeRef<'a>> {
let name_cstr = CString::new(name).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let ret = unsafe {
ffi::lyd_new_inner(
self.raw(),
module
.map(|module| module.as_raw())
.unwrap_or(std::ptr::null_mut()),
name_cstr.as_ptr(),
0,
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(unsafe { DataNodeRef::from_raw(self.tree, rnode) })
}
pub fn new_list(
&mut self,
module: Option<&SchemaModule<'_>>,
name: &str,
keys: &str,
) -> Result<DataNodeRef<'a>> {
let name_cstr = CString::new(name).unwrap();
let keys_cstr = CString::new(keys).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let options = 0;
let ret = unsafe {
ffi::lyd_new_list2(
self.raw(),
module
.map(|module| module.as_raw())
.unwrap_or(std::ptr::null_mut()),
name_cstr.as_ptr(),
keys_cstr.as_ptr(),
options,
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(unsafe { DataNodeRef::from_raw(self.tree, rnode) })
}
pub fn new_list2(
&mut self,
module: Option<&SchemaModule<'_>>,
name: &str,
keys: &[impl AsRef<str>],
) -> Result<DataNodeRef<'a>> {
let name_cstr = CString::new(name).unwrap();
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let options = 0;
let keys: Vec<CString> = keys
.iter()
.map(|key| CString::new(key.as_ref()).unwrap())
.collect();
let keys: Vec<*const c_void> = keys
.iter()
.map(|key| key.as_ptr() as *const c_void)
.collect();
let ret = unsafe {
ffi::lyd_new_list3(
self.raw(),
module
.map(|module| module.as_raw())
.unwrap_or(std::ptr::null_mut()),
name_cstr.as_ptr(),
keys.as_ptr() as *mut *const c_void,
std::ptr::null_mut(),
options,
rnode_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(unsafe { DataNodeRef::from_raw(self.tree, rnode) })
}
pub fn new_term(
&mut self,
module: Option<&SchemaModule<'_>>,
name: &str,
value: Option<&str>,
) -> Result<()> {
let name_cstr = CString::new(name).unwrap();
let value_cstr;
let options = 0;
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_term(
self.raw(),
module
.map(|module| module.as_raw())
.unwrap_or(std::ptr::null_mut()),
name_cstr.as_ptr(),
value_ptr,
options,
std::ptr::null_mut(),
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self.context()));
}
Ok(())
}
pub fn remove(&mut self) {
unsafe { ffi::lyd_unlink_tree(self.raw()) };
unsafe { ffi::lyd_free_tree(self.raw()) };
}
}
impl<'a> Data<'a> for DataNodeRef<'a> {
fn tree(&self) -> &DataTree<'a> {
self.tree
}
fn raw(&self) -> *mut ffi::lyd_node {
self.raw
}
}
unsafe impl<'a> Binding<'a> for DataNodeRef<'a> {
type CType = ffi::lyd_node;
type Container = DataTree<'a>;
unsafe fn from_raw(
tree: &'a DataTree<'a>,
raw: *mut ffi::lyd_node,
) -> DataNodeRef<'a> {
DataNodeRef { tree, raw }
}
}
impl<'a> NodeIterable<'a> for DataNodeRef<'a> {
fn parent(&self) -> Option<DataNodeRef<'a>> {
if self.raw.is_null() {
return None;
}
let parent_inner = unsafe { (*self.raw).parent };
if parent_inner.is_null() {
return None;
}
unsafe { DataNodeRef::from_raw_opt(self.tree, parent_inner) }
}
fn next_sibling(&self) -> Option<DataNodeRef<'a>> {
let rsibling = unsafe { (*self.raw).next };
unsafe { DataNodeRef::from_raw_opt(self.tree, rsibling) }
}
fn first_child(&self) -> Option<DataNodeRef<'a>> {
let snode = unsafe { (*self.raw).schema };
if snode.is_null() {
let ropaq = self.raw as *mut ffi::lyd_node_opaq;
let rchild = unsafe { (*ropaq).child };
return unsafe { DataNodeRef::from_raw_opt(self.tree, rchild) };
}
let nodetype = unsafe { (*snode).nodetype as u32 };
let rchild = match nodetype {
ffi::LYS_CONTAINER
| ffi::LYS_LIST
| ffi::LYS_RPC
| ffi::LYS_ACTION
| ffi::LYS_NOTIF => {
let rinner = self.raw as *mut ffi::lyd_node_inner;
unsafe { (*rinner).child }
}
_ => std::ptr::null_mut(),
};
unsafe { DataNodeRef::from_raw_opt(self.tree, rchild) }
}
}
impl PartialEq for DataNodeRef<'_> {
fn eq(&self, other: &DataNodeRef<'_>) -> bool {
self.raw == other.raw
}
}
unsafe impl Send for DataNodeRef<'_> {}
unsafe impl Sync for DataNodeRef<'_> {}
impl<'a> Metadata<'a> {
pub fn as_raw(&self) -> *mut ffi::lyd_meta {
self.raw
}
pub fn name(&self) -> &str {
char_ptr_to_str(unsafe { (*self.raw).name })
}
pub fn value(&self) -> &str {
let rvalue = unsafe { (*self.raw).value };
let mut canonical = rvalue._canonical;
if canonical.is_null() {
canonical = unsafe {
ffi::lyd_value_get_canonical(
self.dnode.tree.context.raw,
&rvalue,
)
};
}
char_ptr_to_str(canonical)
}
#[doc(hidden)]
pub(crate) fn next(&self) -> Option<Metadata<'a>> {
let rnext = unsafe { (*self.raw).next };
unsafe { Metadata::from_raw_opt(self.dnode, rnext) }
}
}
unsafe impl<'a> Binding<'a> for Metadata<'a> {
type CType = ffi::lyd_meta;
type Container = DataNodeRef<'a>;
unsafe fn from_raw(
dnode: &'a DataNodeRef<'a>,
raw: *mut ffi::lyd_meta,
) -> Metadata<'a> {
Metadata { dnode, raw }
}
}
impl PartialEq for Metadata<'_> {
fn eq(&self, other: &Metadata<'_>) -> bool {
self.raw == other.raw
}
}
unsafe impl Send for Metadata<'_> {}
unsafe impl Sync for Metadata<'_> {}
impl<'a> DataDiff<'a> {
pub fn parse_string(
context: &'a Context,
data: impl AsRef<[u8]>,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataDiff<'a>> {
let dtree = DataTree::parse_string(
context,
data,
format,
parser_options,
validation_options,
)?;
Ok(DataDiff { tree: dtree })
}
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<'a>> {
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: unsafe { DataTree::from_raw(self.tree.context, rnode) },
})
}
}
impl<'a> Data<'a> for DataDiff<'a> {
fn tree(&self) -> &DataTree<'a> {
&self.tree
}
fn raw(&self) -> *mut ffi::lyd_node {
self.tree.raw
}
}