1use bitflags::bitflags;
10use std::collections::HashMap;
11use std::ffi::CString;
12use std::mem::ManuallyDrop;
13use std::os::raw::{c_char, c_void};
14use std::os::unix::ffi::OsStrExt;
15use std::path::Path;
16use std::slice;
17use std::sync::Once;
18
19use crate::error::{Error, Result};
20use crate::iter::{SchemaModules, Set};
21use crate::schema::{SchemaModule, SchemaNode};
22use crate::utils::*;
23use libyang2_sys as ffi;
24
25#[derive(Debug, PartialEq)]
31pub struct Context {
32 pub(crate) raw: *mut ffi::ly_ctx,
33}
34
35bitflags! {
36 pub struct ContextFlags: u16 {
38 const ALL_IMPLEMENTED = ffi::LY_CTX_ALL_IMPLEMENTED as u16;
40
41 const REF_IMPLEMENTED = ffi::LY_CTX_REF_IMPLEMENTED as u16;
49
50 const NO_YANGLIBRARY = ffi::LY_CTX_NO_YANGLIBRARY as u16;
53
54 const DISABLE_SEARCHDIRS = ffi::LY_CTX_DISABLE_SEARCHDIRS as u16;
57
58 const DISABLE_SEARCHDIR_CWD = ffi::LY_CTX_DISABLE_SEARCHDIR_CWD as u16;
62 }
63}
64
65#[derive(Debug, Eq, Hash, PartialEq)]
68pub struct EmbeddedModuleKey {
69 mod_name: &'static str,
70 mod_rev: Option<&'static str>,
71 submod_name: Option<&'static str>,
72 submod_rev: Option<&'static str>,
73}
74
75pub type EmbeddedModules = HashMap<EmbeddedModuleKey, &'static str>;
77
78impl Context {
81 pub fn new(options: ContextFlags) -> Result<Context> {
88 static INIT: Once = Once::new();
89 let mut context = std::ptr::null_mut();
90 let ctx_ptr = &mut context;
91
92 INIT.call_once(|| {
95 unsafe { ffi::ly_log_options(ffi::LY_LOSTORE_LAST) };
98 });
99
100 let ret = unsafe {
101 ffi::ly_ctx_new(std::ptr::null(), options.bits(), ctx_ptr)
102 };
103 if ret != ffi::LY_ERR::LY_SUCCESS {
104 return Err(Error {
106 errcode: ret,
107 msg: None,
108 path: None,
109 apptag: None,
110 });
111 }
112
113 Ok(Context { raw: context })
114 }
115
116 pub fn into_raw(self) -> *mut ffi::ly_ctx {
119 ManuallyDrop::new(self).raw
120 }
121
122 pub fn set_searchdir<P: AsRef<Path>>(
124 &mut self,
125 search_dir: P,
126 ) -> Result<()> {
127 let search_dir =
128 CString::new(search_dir.as_ref().as_os_str().as_bytes()).unwrap();
129 let ret =
130 unsafe { ffi::ly_ctx_set_searchdir(self.raw, search_dir.as_ptr()) };
131 if ret != ffi::LY_ERR::LY_SUCCESS {
132 return Err(Error::new(self));
133 }
134
135 Ok(())
136 }
137
138 pub fn unset_searchdir<P: AsRef<Path>>(
143 &mut self,
144 search_dir: P,
145 ) -> Result<()> {
146 let search_dir =
147 CString::new(search_dir.as_ref().as_os_str().as_bytes()).unwrap();
148 let ret = unsafe {
149 ffi::ly_ctx_unset_searchdir(self.raw, search_dir.as_ptr())
150 };
151 if ret != ffi::LY_ERR::LY_SUCCESS {
152 return Err(Error::new(self));
153 }
154
155 Ok(())
156 }
157
158 pub fn unset_searchdirs(&mut self) -> Result<()> {
160 let ret =
161 unsafe { ffi::ly_ctx_unset_searchdir(self.raw, std::ptr::null()) };
162 if ret != ffi::LY_ERR::LY_SUCCESS {
163 return Err(Error::new(self));
164 }
165
166 Ok(())
167 }
168
169 pub fn unset_searchdir_last(&mut self, count: u32) -> Result<()> {
174 let ret = unsafe { ffi::ly_ctx_unset_searchdir_last(self.raw, count) };
175 if ret != ffi::LY_ERR::LY_SUCCESS {
176 return Err(Error::new(self));
177 }
178
179 Ok(())
180 }
181
182 pub fn set_embedded_modules(&mut self, modules: &EmbeddedModules) {
185 unsafe {
186 ffi::ly_ctx_set_module_imp_clb(
187 self.raw,
188 Some(ly_module_import_cb),
189 modules as *const _ as *mut c_void,
190 )
191 };
192 }
193
194 pub fn unset_embedded_modules(&mut self) {
196 unsafe {
197 ffi::ly_ctx_set_module_imp_clb(self.raw, None, std::ptr::null_mut())
198 };
199 }
200
201 pub fn get_options(&self) -> ContextFlags {
203 let options = unsafe { ffi::ly_ctx_get_options(self.raw) };
204 ContextFlags::from_bits_truncate(options)
205 }
206
207 pub fn set_options(&mut self, options: ContextFlags) -> Result<()> {
209 let ret = unsafe { ffi::ly_ctx_set_options(self.raw, options.bits()) };
210 if ret != ffi::LY_ERR::LY_SUCCESS {
211 return Err(Error::new(self));
212 }
213
214 Ok(())
215 }
216
217 pub fn unset_options(&mut self, options: ContextFlags) -> Result<()> {
219 let ret =
220 unsafe { ffi::ly_ctx_unset_options(self.raw, options.bits()) };
221 if ret != ffi::LY_ERR::LY_SUCCESS {
222 return Err(Error::new(self));
223 }
224
225 Ok(())
226 }
227
228 pub fn get_module_set_id(&self) -> u16 {
230 unsafe { ffi::ly_ctx_get_change_count(self.raw) }
231 }
232
233 pub fn get_module(
238 &self,
239 name: &str,
240 revision: Option<&str>,
241 ) -> Option<SchemaModule<'_>> {
242 let name = CString::new(name).unwrap();
243 let revision_cstr;
244
245 let revision_ptr = match revision {
246 Some(revision) => {
247 revision_cstr = CString::new(revision).unwrap();
248 revision_cstr.as_ptr()
249 }
250 None => std::ptr::null(),
251 };
252 let module = unsafe {
253 ffi::ly_ctx_get_module(self.raw, name.as_ptr(), revision_ptr)
254 };
255 if module.is_null() {
256 return None;
257 }
258
259 Some(unsafe { SchemaModule::from_raw(self, module) })
260 }
261
262 pub fn get_module_latest(&self, name: &str) -> Option<SchemaModule<'_>> {
266 let name = CString::new(name).unwrap();
267 let module =
268 unsafe { ffi::ly_ctx_get_module_latest(self.raw, name.as_ptr()) };
269 if module.is_null() {
270 return None;
271 }
272
273 Some(unsafe { SchemaModule::from_raw(self, module) })
274 }
275
276 pub fn get_module_implemented(
278 &self,
279 name: &str,
280 ) -> Option<SchemaModule<'_>> {
281 let name = CString::new(name).unwrap();
282 let module = unsafe {
283 ffi::ly_ctx_get_module_implemented(self.raw, name.as_ptr())
284 };
285 if module.is_null() {
286 return None;
287 }
288
289 Some(unsafe { SchemaModule::from_raw(self, module) })
290 }
291
292 pub fn get_module_ns(
297 &self,
298 ns: &str,
299 revision: Option<&str>,
300 ) -> Option<SchemaModule<'_>> {
301 let ns = CString::new(ns).unwrap();
302 let revision_cstr;
303
304 let revision_ptr = match revision {
305 Some(revision) => {
306 revision_cstr = CString::new(revision).unwrap();
307 revision_cstr.as_ptr()
308 }
309 None => std::ptr::null(),
310 };
311
312 let module = unsafe {
313 ffi::ly_ctx_get_module_ns(self.raw, ns.as_ptr(), revision_ptr)
314 };
315 if module.is_null() {
316 return None;
317 }
318
319 Some(unsafe { SchemaModule::from_raw(self, module) })
320 }
321
322 pub fn get_module_latest_ns(&self, ns: &str) -> Option<SchemaModule<'_>> {
326 let ns = CString::new(ns).unwrap();
327 let module =
328 unsafe { ffi::ly_ctx_get_module_latest_ns(self.raw, ns.as_ptr()) };
329 if module.is_null() {
330 return None;
331 }
332
333 Some(unsafe { SchemaModule::from_raw(self, module) })
334 }
335
336 pub fn get_module_implemented_ns(
338 &self,
339 ns: &str,
340 ) -> Option<SchemaModule<'_>> {
341 let ns = CString::new(ns).unwrap();
342 let module = unsafe {
343 ffi::ly_ctx_get_module_implemented_ns(self.raw, ns.as_ptr())
344 };
345 if module.is_null() {
346 return None;
347 }
348
349 Some(unsafe { SchemaModule::from_raw(self, module) })
350 }
351
352 pub fn modules(&self, skip_internal: bool) -> SchemaModules<'_> {
357 SchemaModules::new(self, skip_internal)
358 }
359
360 pub fn traverse(&self) -> impl Iterator<Item = SchemaNode<'_>> {
363 self.modules(false).flat_map(|module| module.traverse())
364 }
365
366 pub fn internal_module_count(&self) -> u32 {
369 unsafe { ffi::ly_ctx_internal_modules_count(self.raw) }
370 }
371
372 pub fn load_module(
388 &mut self,
389 name: &str,
390 revision: Option<&str>,
391 features: &[&str],
392 ) -> Result<SchemaModule<'_>> {
393 let name = CString::new(name).unwrap();
394 let revision_cstr;
395 let mut features_ptr;
396
397 let revision_ptr = match revision {
399 Some(revision) => {
400 revision_cstr = CString::new(revision).unwrap();
401 revision_cstr.as_ptr()
402 }
403 None => std::ptr::null(),
404 };
405
406 let features_cstr = features
408 .iter()
409 .map(|feature| CString::new(*feature).unwrap())
410 .collect::<Vec<_>>();
411 features_ptr = features_cstr
412 .iter()
413 .map(|feature| feature.as_ptr())
414 .collect::<Vec<_>>();
415 features_ptr.push(std::ptr::null());
416
417 let module = unsafe {
418 ffi::ly_ctx_load_module(
419 self.raw,
420 name.as_ptr(),
421 revision_ptr,
422 features_ptr.as_mut_ptr(),
423 )
424 };
425 if module.is_null() {
426 return Err(Error::new(self));
427 }
428
429 Ok(unsafe { SchemaModule::from_raw(self, module as *mut _) })
430 }
431
432 pub fn find_xpath(&self, path: &str) -> Result<Set<'_, SchemaNode<'_>>> {
434 let path = CString::new(path).unwrap();
435 let mut set = std::ptr::null_mut();
436 let set_ptr = &mut set;
437 let options = 0u32;
438
439 let ret = unsafe {
440 ffi::lys_find_xpath(
441 self.raw,
442 std::ptr::null(),
443 path.as_ptr(),
444 options,
445 set_ptr,
446 )
447 };
448 if ret != ffi::LY_ERR::LY_SUCCESS {
449 return Err(Error::new(self));
450 }
451
452 let rnodes_count = unsafe { (*set).count } as usize;
453 let slice = if rnodes_count == 0 {
454 &[]
455 } else {
456 let rnodes = unsafe { (*set).__bindgen_anon_1.snodes };
457 unsafe { slice::from_raw_parts(rnodes, rnodes_count) }
458 };
459
460 Ok(Set::new(self, slice))
461 }
462
463 pub fn find_path(&self, path: &str) -> Result<SchemaNode<'_>> {
465 let path = CString::new(path).unwrap();
466
467 let rnode = unsafe {
468 ffi::lys_find_path(self.raw, std::ptr::null(), path.as_ptr(), 0)
469 };
470 if rnode.is_null() {
471 return Err(Error::new(self));
472 }
473
474 Ok(unsafe { SchemaNode::from_raw(self, rnode as *mut _) })
475 }
476}
477
478unsafe impl Send for Context {}
479unsafe impl Sync for Context {}
480
481impl Drop for Context {
482 fn drop(&mut self) {
483 unsafe { ffi::ly_ctx_destroy(self.raw) };
484 }
485}
486
487impl EmbeddedModuleKey {
490 pub fn new(
491 mod_name: &'static str,
492 mod_rev: Option<&'static str>,
493 submod_name: Option<&'static str>,
494 submod_rev: Option<&'static str>,
495 ) -> EmbeddedModuleKey {
496 EmbeddedModuleKey {
497 mod_name,
498 mod_rev,
499 submod_name,
500 submod_rev,
501 }
502 }
503}
504
505unsafe impl<'a> Binding<'a> for Context {
506 type CType = ffi::ly_ctx;
507 type Container = ();
508
509 unsafe fn from_raw(_: &'a Self::Container, raw: *mut Self::CType) -> Self {
510 Self { raw }
511 }
512}
513
514fn find_embedded_module<'a>(
517 modules: &'a EmbeddedModules,
518 mod_name: &'a str,
519 mod_rev: Option<&'a str>,
520 submod_name: Option<&'a str>,
521 submod_rev: Option<&'a str>,
522) -> Option<(&'a EmbeddedModuleKey, &'a &'a str)> {
523 modules.iter().find(|(key, _)| {
524 *key.mod_name == *mod_name
525 && (mod_rev.is_none() || key.mod_rev == mod_rev)
526 && match submod_name {
527 Some(submod_name) => {
528 key.submod_name == Some(submod_name)
529 && (submod_rev.is_none()
530 || key.submod_rev == submod_rev)
531 }
532 None => key.submod_name.is_none(),
533 }
534 })
535}
536
537unsafe extern "C" fn ly_module_import_cb(
538 mod_name: *const c_char,
539 mod_rev: *const c_char,
540 submod_name: *const c_char,
541 submod_rev: *const c_char,
542 user_data: *mut c_void,
543 format: *mut ffi::LYS_INFORMAT::Type,
544 module_data: *mut *const c_char,
545 _free_module_data: *mut ffi::ly_module_imp_data_free_clb,
546) -> ffi::LY_ERR::Type {
547 let modules = &*(user_data as *const EmbeddedModules);
548 let mod_name = char_ptr_to_str(mod_name);
549 let mod_rev = char_ptr_to_opt_str(mod_rev);
550 let submod_name = char_ptr_to_opt_str(submod_name);
551 let submod_rev = char_ptr_to_opt_str(submod_rev);
552
553 if let Some((_emod_key, emod_data)) = find_embedded_module(
554 modules,
555 mod_name,
556 mod_rev,
557 submod_name,
558 submod_rev,
559 ) {
560 let data = CString::new(*emod_data).unwrap();
561
562 *format = ffi::LYS_INFORMAT::LYS_IN_YANG;
563 *module_data = data.as_ptr();
564 std::mem::forget(data);
565 return ffi::LY_ERR::LY_SUCCESS;
566 }
567
568 ffi::LY_ERR::LY_ENOTFOUND
569}