use bitflags::bitflags;
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::os::unix::io::AsRawFd;
use std::slice;
use std::sync::Arc;
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 libyang2_sys as ffi;
#[derive(Debug)]
pub struct DataTree {
context: Arc<Context>,
raw: *mut ffi::lyd_node,
}
#[derive(Clone, Debug)]
pub struct DataNodeRef<'a> {
tree: &'a DataTree,
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 {
tree: DataTree,
}
#[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,
}
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;
}
}
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 {
#[doc(hidden)]
fn context(&self) -> &Arc<Context> {
&self.tree().context
}
#[doc(hidden)]
fn tree(&self) -> &DataTree;
#[doc(hidden)]
fn raw(&self) -> *mut ffi::lyd_node;
fn find_xpath(&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_path(&self, path: &str) -> Result<DataNodeRef<'_>> {
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(DataNodeRef::from_raw(self.tree(), rnode as *mut _))
}
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<Option<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_opt_string(cstr))
}
}
impl DataTree {
pub fn new(context: &Arc<Context>) -> DataTree {
DataTree {
context: context.clone(),
raw: std::ptr::null_mut(),
}
}
pub fn parse_file<F: AsRawFd>(
context: &Arc<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: &Arc<Context>,
data: &str,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree> {
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 parse_op_string(
context: &Arc<Context>,
data: &str,
format: DataFormat,
op: DataOperation,
) -> Result<DataTree> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let data = CString::new(data).unwrap();
let mut ly_in = std::ptr::null_mut();
let ret = unsafe { ffi::ly_in_new_memory(data.as_ptr(), &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,
rnode_ptr,
std::ptr::null_mut(),
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(context));
}
Ok(DataTree::from_raw(context, rnode))
}
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, value_len) = match value {
Some(value) => {
value_cstr = CString::new(value).unwrap();
(value_cstr.as_ptr(), value.len())
}
None => (std::ptr::null(), 0),
};
let mut options = ffi::LYD_NEW_PATH_UPDATE;
if output {
options |= ffi::LYD_NEW_PATH_OUTPUT;
}
let ret = unsafe {
ffi::lyd_new_path2(
self.raw(),
self.context().raw,
path.as_ptr(),
value_ptr as *const c_void,
value_len as u64,
ffi::LYD_ANYDATA_VALUETYPE::LYD_ANYDATA_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(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(&self) -> Result<DataTree> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;
if self.raw.is_null() {
return Ok(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(DataTree::from_raw(&self.context, dup))
}
pub fn merge(&mut self, source: &DataTree) -> Result<()> {
if self.raw.is_null() {
*self = source.duplicate()?;
} 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,
options: DataDiffFlags,
) -> Result<DataDiff> {
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: 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(&self) -> impl Iterator<Item = DataNodeRef<'_>> {
let top = Siblings::new(self.reference());
top.flat_map(|dnode| dnode.traverse())
}
}
impl Data for DataTree {
fn tree(&self) -> &DataTree {
self
}
fn raw(&self) -> *mut ffi::lyd_node {
self.raw
}
}
impl<'a> Binding<'a> for DataTree {
type CType = ffi::lyd_node;
type Container = Arc<Context>;
fn from_raw(
context: &'a Arc<Context>,
raw: *mut ffi::lyd_node,
) -> DataTree {
DataTree {
context: context.clone(),
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> 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 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 = 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() 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_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)
}
_ => 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(&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> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;
if self.raw.is_null() {
return Ok(DataTree::from_raw(
self.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()));
}
Ok(DataTree::from_raw(self.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_)
}
}
}
impl<'a> Data for DataNodeRef<'a> {
fn tree(&self) -> &DataTree {
self.tree
}
fn raw(&self) -> *mut ffi::lyd_node {
self.raw
}
}
impl<'a> Binding<'a> for DataNodeRef<'a> {
type CType = ffi::lyd_node;
type Container = DataTree;
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 { &mut (*(*self.raw).parent).__bindgen_anon_1.node };
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 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 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(),
};
DataNodeRef::from_raw_opt(self.tree, rchild)
}
}
impl<'a> PartialEq for DataNodeRef<'a> {
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 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
}
}
unsafe impl Send for Metadata<'_> {}
unsafe impl Sync for Metadata<'_> {}
impl DataDiff {
pub fn parse_string(
context: &Arc<Context>,
data: &str,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataDiff> {
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> {
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 Data for DataDiff {
fn tree(&self) -> &DataTree {
&self.tree
}
fn raw(&self) -> *mut ffi::lyd_node {
self.tree.raw
}
}