Skip to content

Commit 4a2abdf

Browse files
authored
Merge pull request #42 from rodonile/imports
Add Schema Import support
2 parents c2d11c6 + e71d466 commit 4a2abdf

3 files changed

Lines changed: 153 additions & 0 deletions

File tree

examples/schema_imports.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use yang3::context::{Context, ContextFlags};
2+
3+
static SEARCH_DIR: &str = "./assets/yang/";
4+
static MODULE_NAME: &str = "ietf-isis";
5+
6+
fn main() -> std::io::Result<()> {
7+
// Initialize context
8+
let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY)
9+
.expect("Failed to create context");
10+
11+
// Set search directory
12+
ctx.set_searchdir(SEARCH_DIR)
13+
.expect("Failed to set YANG search directory");
14+
15+
// Load the module
16+
let module = ctx
17+
.load_module(MODULE_NAME, None, &[])
18+
.expect("Failed to load module");
19+
20+
// Get imports for the loaded module
21+
let imports: Vec<_> = module.imports().collect();
22+
23+
println!("Module '{}' imports:\n", module.name());
24+
25+
// Check methods
26+
for import in &imports {
27+
println!(" Import: {}", import.name());
28+
println!(" Prefix: {}", import.prefix());
29+
30+
if let Some(description) = import.description() {
31+
println!(" Description: {}", description);
32+
}
33+
34+
if let Some(reference) = import.reference() {
35+
let reference_oneline =
36+
reference.replace('\n', " ").replace('\r', " ");
37+
println!(" Reference: {}", reference_oneline);
38+
}
39+
40+
// Check module() method
41+
let imported_module = import.module();
42+
println!(" Name: {}", imported_module.name());
43+
println!(" Namespace: {}", imported_module.namespace());
44+
45+
if let Some(filepath) = imported_module.filepath() {
46+
println!(" File path: {}", filepath);
47+
}
48+
49+
if let Some(revision) = imported_module.revision() {
50+
println!(" Revision: {}", revision);
51+
}
52+
println!()
53+
}
54+
55+
Ok(())
56+
}

src/schema.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ pub struct SchemaSubmodule<'a> {
3838
pub(crate) raw: *mut ffi::lysp_submodule,
3939
}
4040

41+
/// Available YANG schema tree structures representing YANG import.
42+
#[derive(Clone, Debug)]
43+
pub struct SchemaImport<'a> {
44+
pub(crate) context: &'a Context,
45+
pub(crate) raw: *mut ffi::lysp_import,
46+
}
47+
4148
/// Schema input formats accepted by libyang.
4249
#[allow(clippy::upper_case_acronyms)]
4350
#[repr(u32)]
@@ -443,6 +450,17 @@ impl<'a> SchemaModule<'a> {
443450
self.notifications().flat_map(|snode| snode.traverse());
444451
data.chain(rpcs).chain(notifications)
445452
}
453+
454+
/// Returns an iterator over the list of imports.
455+
pub fn imports(&self) -> impl Iterator<Item = SchemaImport<'a>> {
456+
let parsed = unsafe { (*self.raw).parsed };
457+
if parsed.is_null() {
458+
return Array::new(self.context, std::ptr::null_mut(), 0);
459+
}
460+
let array = unsafe { (*parsed).imports };
461+
let ptr_size = mem::size_of::<ffi::lysp_import>();
462+
Array::new(self.context, array as *mut _, ptr_size)
463+
}
446464
}
447465

448466
unsafe impl<'a> Binding<'a> for SchemaModule<'a> {
@@ -529,6 +547,51 @@ impl PartialEq for SchemaSubmodule<'_> {
529547
unsafe impl Send for SchemaSubmodule<'_> {}
530548
unsafe impl Sync for SchemaSubmodule<'_> {}
531549

550+
// ===== impl SchemaImport =====
551+
552+
impl<'a> SchemaImport<'a> {
553+
/// Import Module.
554+
pub fn module(&self) -> SchemaModule<'_> {
555+
let module = unsafe { (*self.raw).module };
556+
unsafe { SchemaModule::from_raw(self.context, module) }
557+
}
558+
559+
/// Import module name.
560+
pub fn name(&self) -> &str {
561+
char_ptr_to_str(unsafe { (*self.raw).name })
562+
}
563+
564+
/// Prefix used to reference definitions from the import module.
565+
pub fn prefix(&self) -> &str {
566+
char_ptr_to_str(unsafe { (*self.raw).prefix })
567+
}
568+
569+
/// Description of the import.
570+
pub fn description(&self) -> Option<&str> {
571+
char_ptr_to_opt_str(unsafe { (*self.raw).dsc })
572+
}
573+
574+
/// Cross-reference for the import.
575+
pub fn reference(&self) -> Option<&str> {
576+
char_ptr_to_opt_str(unsafe { (*self.raw).ref_ })
577+
}
578+
}
579+
580+
unsafe impl<'a> Binding<'a> for SchemaImport<'a> {
581+
type CType = ffi::lysp_import;
582+
type Container = Context;
583+
584+
unsafe fn from_raw(
585+
context: &'a Context,
586+
raw: *mut ffi::lysp_import,
587+
) -> SchemaImport<'a> {
588+
SchemaImport { context, raw }
589+
}
590+
}
591+
592+
unsafe impl Send for SchemaImport<'_> {}
593+
unsafe impl Sync for SchemaImport<'_> {}
594+
532595
// ===== impl SchemaNode =====
533596

534597
impl<'a> SchemaNode<'a> {

tests/schema.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,3 +634,37 @@ fn test_extensions_uncompiled_modules() {
634634
let extensions = module.extensions().collect::<Vec<_>>();
635635
assert_eq!(extensions.len(), 0);
636636
}
637+
638+
#[test]
639+
fn schema_module_imports() {
640+
let mut ctx = create_context();
641+
642+
// Test a module that has imports
643+
let module = ctx.get_module_latest("ietf-interfaces").unwrap();
644+
let imports: Vec<_> = module.imports().collect();
645+
646+
// ietf-interfaces imports only ietf-yang-types
647+
assert_eq!(imports.len(), 1);
648+
649+
// Test the ietf-yang-types import
650+
let yang_types_import = &imports[0];
651+
assert_eq!(yang_types_import.name(), "ietf-yang-types");
652+
assert_eq!(yang_types_import.prefix(), "yang");
653+
assert_eq!(yang_types_import.reference(), None);
654+
655+
// Test imported module access
656+
let imported_module = yang_types_import.module();
657+
assert_eq!(imported_module.name(), "ietf-yang-types");
658+
assert_eq!(
659+
imported_module.namespace(),
660+
"urn:ietf:params:xml:ns:yang:ietf-yang-types"
661+
);
662+
assert_eq!(imported_module.reference(), None);
663+
664+
// Test a module with no imports (like ietf-restconf)
665+
let module = ctx
666+
.load_module("ietf-restconf", None, &[])
667+
.expect("Failed to load module");
668+
let module_imports: Vec<_> = module.imports().collect();
669+
assert_eq!(module_imports.len(), 0);
670+
}

0 commit comments

Comments
 (0)