use bitflags::bitflags;
use std::collections::HashMap;
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::slice;
use crate::error::{Error, Result};
use crate::iter::{SchemaModules, Set};
use crate::schema::{SchemaModule, SchemaNode};
use crate::utils::*;
use libyang2_sys as ffi;
#[derive(Debug, PartialEq)]
pub struct Context {
pub(crate) raw: *mut ffi::ly_ctx,
}
bitflags! {
pub struct ContextFlags: u16 {
const ALL_IMPLEMENTED = ffi::LY_CTX_ALL_IMPLEMENTED as u16;
const REF_IMPLEMENTED = ffi::LY_CTX_REF_IMPLEMENTED as u16;
const NO_YANGLIBRARY = ffi::LY_CTX_NO_YANGLIBRARY as u16;
const DISABLE_SEARCHDIRS = ffi::LY_CTX_DISABLE_SEARCHDIRS as u16;
const DISABLE_SEARCHDIR_CWD = ffi::LY_CTX_DISABLE_SEARCHDIR_CWD as u16;
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct EmbeddedModuleKey {
mod_name: &'static str,
mod_rev: Option<&'static str>,
submod_name: Option<&'static str>,
submod_rev: Option<&'static str>,
}
pub type EmbeddedModules = HashMap<EmbeddedModuleKey, &'static str>;
impl Context {
pub fn new(options: ContextFlags) -> Result<Context> {
let mut context = std::ptr::null_mut();
let ctx_ptr = &mut context;
let ret =
unsafe { ffi::ly_ctx_new(std::ptr::null(), options.bits, ctx_ptr) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error {
errcode: ret,
msg: None,
path: None,
apptag: None,
});
}
Ok(Context { raw: context })
}
pub fn set_searchdir<P: AsRef<Path>>(
&mut self,
search_dir: P,
) -> Result<()> {
let search_dir =
CString::new(search_dir.as_ref().as_os_str().as_bytes()).unwrap();
let ret =
unsafe { ffi::ly_ctx_set_searchdir(self.raw, search_dir.as_ptr()) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn unset_searchdir<P: AsRef<Path>>(
&mut self,
search_dir: P,
) -> Result<()> {
let search_dir =
CString::new(search_dir.as_ref().as_os_str().as_bytes()).unwrap();
let ret = unsafe {
ffi::ly_ctx_unset_searchdir(self.raw, search_dir.as_ptr())
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn unset_searchdirs(&mut self) -> Result<()> {
let ret =
unsafe { ffi::ly_ctx_unset_searchdir(self.raw, std::ptr::null()) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn unset_searchdir_last(&mut self, count: u32) -> Result<()> {
let ret = unsafe { ffi::ly_ctx_unset_searchdir_last(self.raw, count) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn set_embedded_modules(&mut self, modules: &EmbeddedModules) {
unsafe {
ffi::ly_ctx_set_module_imp_clb(
self.raw,
Some(ly_module_import_cb),
modules as *const _ as *mut c_void,
)
};
}
pub fn unset_embedded_modules(&mut self) {
unsafe {
ffi::ly_ctx_set_module_imp_clb(self.raw, None, std::ptr::null_mut())
};
}
pub fn get_options(&self) -> ContextFlags {
let options = unsafe { ffi::ly_ctx_get_options(self.raw) };
ContextFlags::from_bits_truncate(options)
}
pub fn set_options(&mut self, options: ContextFlags) -> Result<()> {
let ret = unsafe { ffi::ly_ctx_set_options(self.raw, options.bits) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn unset_options(&mut self, options: ContextFlags) -> Result<()> {
let ret = unsafe { ffi::ly_ctx_unset_options(self.raw, options.bits) };
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
Ok(())
}
pub fn get_module_set_id(&self) -> u16 {
unsafe { ffi::ly_ctx_get_change_count(self.raw) }
}
pub fn get_module(
&self,
name: &str,
revision: Option<&str>,
) -> Option<SchemaModule<'_>> {
let name = CString::new(name).unwrap();
let revision_cstr;
let revision_ptr = match revision {
Some(revision) => {
revision_cstr = CString::new(revision).unwrap();
revision_cstr.as_ptr()
}
None => std::ptr::null(),
};
let module = unsafe {
ffi::ly_ctx_get_module(self.raw, name.as_ptr(), revision_ptr)
};
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn get_module_latest(&self, name: &str) -> Option<SchemaModule<'_>> {
let name = CString::new(name).unwrap();
let module =
unsafe { ffi::ly_ctx_get_module_latest(self.raw, name.as_ptr()) };
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn get_module_implemented(
&self,
name: &str,
) -> Option<SchemaModule<'_>> {
let name = CString::new(name).unwrap();
let module = unsafe {
ffi::ly_ctx_get_module_implemented(self.raw, name.as_ptr())
};
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn get_module_ns(
&self,
ns: &str,
revision: Option<&str>,
) -> Option<SchemaModule<'_>> {
let ns = CString::new(ns).unwrap();
let revision_cstr;
let revision_ptr = match revision {
Some(revision) => {
revision_cstr = CString::new(revision).unwrap();
revision_cstr.as_ptr()
}
None => std::ptr::null(),
};
let module = unsafe {
ffi::ly_ctx_get_module_ns(self.raw, ns.as_ptr(), revision_ptr)
};
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn get_module_latest_ns(&self, ns: &str) -> Option<SchemaModule<'_>> {
let ns = CString::new(ns).unwrap();
let module =
unsafe { ffi::ly_ctx_get_module_latest_ns(self.raw, ns.as_ptr()) };
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn get_module_implemented_ns(
&self,
ns: &str,
) -> Option<SchemaModule<'_>> {
let ns = CString::new(ns).unwrap();
let module = unsafe {
ffi::ly_ctx_get_module_implemented_ns(self.raw, ns.as_ptr())
};
if module.is_null() {
return None;
}
Some(SchemaModule::from_raw(self, module))
}
pub fn modules(&self, skip_internal: bool) -> SchemaModules<'_> {
SchemaModules::new(self, skip_internal)
}
pub fn traverse(&self) -> impl Iterator<Item = SchemaNode<'_>> {
self.modules(false).flat_map(|module| module.traverse())
}
pub fn reset_latests(&mut self) {
unsafe { ffi::ly_ctx_reset_latests(self.raw) };
}
pub fn internal_module_count(&self) -> u32 {
unsafe { ffi::ly_ctx_internal_modules_count(self.raw) }
}
pub fn load_module(
&mut self,
name: &str,
revision: Option<&str>,
features: &[&str],
) -> Result<SchemaModule<'_>> {
let name = CString::new(name).unwrap();
let revision_cstr;
let features_cstr;
let mut features_ptr;
let revision_ptr = match revision {
Some(revision) => {
revision_cstr = CString::new(revision).unwrap();
revision_cstr.as_ptr()
}
None => std::ptr::null(),
};
features_cstr = features
.iter()
.map(|feature| CString::new(*feature).unwrap())
.collect::<Vec<_>>();
features_ptr = features_cstr
.iter()
.map(|feature| feature.as_ptr())
.collect::<Vec<_>>();
features_ptr.push(std::ptr::null());
let module = unsafe {
ffi::ly_ctx_load_module(
self.raw,
name.as_ptr(),
revision_ptr,
features_ptr.as_mut_ptr(),
)
};
if module.is_null() {
return Err(Error::new(self));
}
Ok(SchemaModule::from_raw(self, module as *mut _))
}
pub fn find_xpath(&self, path: &str) -> Result<Set<'_, SchemaNode<'_>>> {
let path = CString::new(path).unwrap();
let mut set = std::ptr::null_mut();
let set_ptr = &mut set;
let options = 0u32;
let ret = unsafe {
ffi::lys_find_xpath(
self.raw,
std::ptr::null(),
path.as_ptr(),
options,
set_ptr,
)
};
if ret != ffi::LY_ERR::LY_SUCCESS {
return Err(Error::new(self));
}
let rnodes_count = unsafe { (*set).count } as usize;
let slice = if rnodes_count == 0 {
&[]
} else {
let rnodes = unsafe { (*set).__bindgen_anon_1.snodes };
unsafe { slice::from_raw_parts(rnodes, rnodes_count) }
};
Ok(Set::new(self, slice))
}
pub fn find_path(&self, path: &str) -> Result<SchemaNode<'_>> {
let path = CString::new(path).unwrap();
let rnode = unsafe {
ffi::lys_find_path(self.raw, std::ptr::null(), path.as_ptr(), 0)
};
if rnode.is_null() {
return Err(Error::new(self));
}
Ok(SchemaNode::from_raw(self, rnode as *mut _))
}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
impl Drop for Context {
fn drop(&mut self) {
unsafe { ffi::ly_ctx_destroy(self.raw) };
}
}
impl EmbeddedModuleKey {
pub fn new(
mod_name: &'static str,
mod_rev: Option<&'static str>,
submod_name: Option<&'static str>,
submod_rev: Option<&'static str>,
) -> EmbeddedModuleKey {
EmbeddedModuleKey {
mod_name,
mod_rev,
submod_name,
submod_rev,
}
}
}
fn find_embedded_module<'a>(
modules: &'a EmbeddedModules,
mod_name: &'a str,
mod_rev: Option<&'a str>,
submod_name: Option<&'a str>,
submod_rev: Option<&'a str>,
) -> Option<(&'a EmbeddedModuleKey, &'a &'a str)> {
modules.iter().find(|(key, _)| {
if *key.mod_name != *mod_name {
return false;
}
if let Some(mod_rev) = &mod_rev {
if let Some(emod_rev) = &key.mod_rev {
if *emod_rev != *mod_rev {
return false;
}
}
}
if let Some(submod_name) = &submod_name {
if let Some(esubmod_name) = &key.submod_name {
if *esubmod_name != *submod_name {
return false;
}
}
if let Some(submod_rev) = &submod_rev {
if let Some(esubmod_rev) = &key.submod_rev {
if *esubmod_rev != *submod_rev {
return false;
}
}
}
}
true
})
}
unsafe extern "C" fn ly_module_import_cb(
mod_name: *const c_char,
mod_rev: *const c_char,
submod_name: *const c_char,
submod_rev: *const c_char,
user_data: *mut c_void,
format: *mut ffi::LYS_INFORMAT::Type,
module_data: *mut *const c_char,
_free_module_data: *mut ffi::ly_module_imp_data_free_clb,
) -> ffi::LY_ERR::Type {
let modules = &*(user_data as *const EmbeddedModules);
let mod_name = char_ptr_to_str(mod_name);
let mod_rev = char_ptr_to_opt_str(mod_rev);
let submod_name = char_ptr_to_opt_str(submod_name);
let submod_rev = char_ptr_to_opt_str(submod_rev);
if let Some((_emod_key, emod_data)) = find_embedded_module(
modules,
mod_name,
mod_rev,
submod_name,
submod_rev,
) {
let data = CString::new(*emod_data).unwrap();
*format = ffi::LYS_INFORMAT::LYS_IN_YANG;
*module_data = data.as_ptr();
std::mem::forget(data);
return ffi::LY_ERR::LY_SUCCESS;
}
ffi::LY_ERR::LY_ENOTFOUND
}