yang2/
schema.rs

1//
2// Copyright (c) The yang2-rs Core Contributors
3//
4// SPDX-License-Identifier: MIT
5//
6
7//! YANG schema data.
8
9use bitflags::bitflags;
10use num_derive::FromPrimitive;
11use num_traits::FromPrimitive;
12use std::ffi::CString;
13use std::mem;
14use std::os::raw::{c_char, c_void};
15use std::os::unix::io::AsRawFd;
16use std::slice;
17
18use crate::context::Context;
19use crate::data::DataTree;
20use crate::error::{Error, Result};
21use crate::iter::{
22    Ancestors, Array, Getnext, IterSchemaFlags, NodeIterable, Set, Siblings,
23    Traverse,
24};
25use crate::utils::*;
26use libyang2_sys as ffi;
27
28/// Available YANG schema tree structures representing YANG module.
29#[derive(Clone, Debug)]
30pub struct SchemaModule<'a> {
31    pub(crate) context: &'a Context,
32    pub(crate) raw: *mut ffi::lys_module,
33}
34
35/// Schema input formats accepted by libyang.
36#[allow(clippy::upper_case_acronyms)]
37#[repr(u32)]
38#[derive(Clone, Copy, Debug, PartialEq)]
39pub enum SchemaInputFormat {
40    YANG = ffi::LYS_INFORMAT::LYS_IN_YANG,
41    YIN = ffi::LYS_INFORMAT::LYS_IN_YIN,
42}
43
44/// Schema output formats accepted by libyang.
45#[allow(clippy::upper_case_acronyms)]
46#[repr(u32)]
47#[derive(Clone, Copy, Debug, PartialEq)]
48pub enum SchemaOutputFormat {
49    YANG = ffi::LYS_OUTFORMAT::LYS_OUT_YANG,
50    YIN = ffi::LYS_OUTFORMAT::LYS_OUT_YIN,
51    TREE = ffi::LYS_OUTFORMAT::LYS_OUT_TREE,
52}
53
54/// Schema path format.
55#[allow(clippy::upper_case_acronyms)]
56#[repr(u32)]
57#[derive(Clone, Copy, Debug, PartialEq)]
58pub enum SchemaPathFormat {
59    /// Descriptive path format used in log messages.
60    LOG = ffi::LYSC_PATH_TYPE::LYSC_PATH_LOG,
61    /// Similar to LOG except that schema-only nodes (choice, case) are
62    /// skipped.
63    DATA = ffi::LYSC_PATH_TYPE::LYSC_PATH_DATA,
64}
65
66bitflags! {
67    /// Schema printer flags.
68    pub struct SchemaPrinterFlags: u32 {
69        /// Flag for output without indentation and formatting new lines.
70        const SHRINK = ffi::LYS_PRINT_SHRINK;
71        /// Print only top-level/reference node information, do not print
72        /// information from the substatements.
73        const NO_SUBSTMT = ffi::LYS_PRINT_NO_SUBSTMT;
74    }
75}
76
77/// Generic YANG schema node.
78#[derive(Clone, Debug)]
79pub struct SchemaNode<'a> {
80    pub(crate) context: &'a Context,
81    pub(crate) raw: *mut ffi::lysc_node,
82    kind: SchemaNodeKind,
83}
84
85/// YANG schema node kind.
86#[derive(Clone, Copy, Debug, PartialEq)]
87pub enum SchemaNodeKind {
88    Container,
89    Case,
90    Choice,
91    Leaf,
92    LeafList,
93    List,
94    AnyData,
95    Rpc,
96    Input,
97    Output,
98    Action,
99    Notification,
100}
101
102/// YANG must substatement.
103#[derive(Clone, Debug)]
104pub struct SchemaStmtMust<'a> {
105    raw: *mut ffi::lysc_must,
106    _marker: std::marker::PhantomData<&'a Context>,
107}
108
109/// YANG when substatement.
110#[derive(Clone, Debug)]
111pub struct SchemaStmtWhen<'a> {
112    raw: *mut ffi::lysc_when,
113    _marker: std::marker::PhantomData<&'a Context>,
114}
115
116/// YANG leaf(-list) type.
117#[derive(Clone, Debug)]
118pub struct SchemaLeafType<'a> {
119    context: &'a Context,
120    raw: *mut ffi::lysc_type,
121}
122
123/// YANG extension instance.
124#[derive(Clone, Debug)]
125pub struct SchemaExtInstance<'a> {
126    context: &'a Context,
127    raw: *mut ffi::lysc_ext_instance,
128}
129
130/// YANG data value type.
131#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
132pub enum DataValueType {
133    Unknown = 0,
134    Binary = 1,
135    Uint8 = 2,
136    Uint16 = 3,
137    Uint32 = 4,
138    Uint64 = 5,
139    String = 6,
140    Bits = 7,
141    Bool = 8,
142    Dec64 = 9,
143    Empty = 10,
144    Enum = 11,
145    IdentityRef = 12,
146    InstanceId = 13,
147    LeafRef = 14,
148    Union = 15,
149    Int8 = 16,
150    Int16 = 17,
151    Int32 = 18,
152    Int64 = 19,
153}
154
155/// YANG data value.
156#[derive(Clone, Debug, PartialEq)]
157pub enum DataValue {
158    Uint8(u8),
159    Uint16(u16),
160    Uint32(u32),
161    Uint64(u64),
162    Bool(bool),
163    Empty,
164    Int8(i8),
165    Int16(i16),
166    Int32(i32),
167    Int64(i64),
168    Other(String),
169}
170
171// ===== impl SchemaModule =====
172
173impl<'a> SchemaModule<'a> {
174    /// Returns a mutable raw pointer to the underlying C library representation
175    /// of the module.
176    pub fn as_raw(&self) -> *mut ffi::lys_module {
177        self.raw
178    }
179
180    /// Name of the module.
181    pub fn name(&self) -> &str {
182        char_ptr_to_str(unsafe { (*self.raw).name })
183    }
184
185    /// Revision of the module.
186    pub fn revision(&self) -> Option<&str> {
187        char_ptr_to_opt_str(unsafe { (*self.raw).revision })
188    }
189
190    /// Namespace of the module.
191    pub fn namespace(&self) -> &str {
192        char_ptr_to_str(unsafe { (*self.raw).ns })
193    }
194
195    /// Prefix of the module.
196    pub fn prefix(&self) -> &str {
197        char_ptr_to_str(unsafe { (*self.raw).prefix })
198    }
199
200    /// File path, if the schema was read from a file.
201    pub fn filepath(&self) -> Option<&str> {
202        char_ptr_to_opt_str(unsafe { (*self.raw).filepath })
203    }
204
205    /// Party/company responsible for the module.
206    pub fn organization(&self) -> Option<&str> {
207        char_ptr_to_opt_str(unsafe { (*self.raw).org })
208    }
209
210    /// Contact information for the module.
211    pub fn contact(&self) -> Option<&str> {
212        char_ptr_to_opt_str(unsafe { (*self.raw).contact })
213    }
214
215    /// Description of the module.
216    pub fn description(&self) -> Option<&str> {
217        char_ptr_to_opt_str(unsafe { (*self.raw).dsc })
218    }
219
220    /// Cross-reference for the module.
221    pub fn reference(&self) -> Option<&str> {
222        char_ptr_to_opt_str(unsafe { (*self.raw).ref_ })
223    }
224
225    /// Make the specific module implemented.
226    pub fn set_implemented(&self) -> Result<()> {
227        let ret =
228            unsafe { ffi::lys_set_implemented(self.raw, std::ptr::null_mut()) };
229        if ret != ffi::LY_ERR::LY_SUCCESS {
230            return Err(Error::new(self.context));
231        }
232
233        Ok(())
234    }
235
236    /// Return true if the module is implemented, not just imported.
237    pub fn is_implemented(&self) -> bool {
238        unsafe { (*self.raw).implemented != 0 }
239    }
240
241    /// Get the current real status of the specified feature in the module.
242    pub fn feature_value(&self, feature: &str) -> Result<bool> {
243        let feature = CString::new(feature).unwrap();
244        let ret = unsafe { ffi::lys_feature_value(self.raw, feature.as_ptr()) };
245        match ret {
246            ffi::LY_ERR::LY_SUCCESS => Ok(true),
247            ffi::LY_ERR::LY_ENOT => Ok(false),
248            _ => Err(Error::new(self.context)),
249        }
250    }
251
252    /// Print schema tree in the specified format into a file descriptor.
253    pub fn print_file<F: AsRawFd>(
254        &self,
255        fd: F,
256        format: SchemaOutputFormat,
257        options: SchemaPrinterFlags,
258    ) -> Result<()> {
259        let ret = unsafe {
260            ffi::lys_print_fd(
261                fd.as_raw_fd(),
262                self.raw,
263                format as u32,
264                options.bits(),
265            )
266        };
267        if ret != ffi::LY_ERR::LY_SUCCESS {
268            return Err(Error::new(self.context));
269        }
270
271        Ok(())
272    }
273
274    /// Print schema tree in the specified format into a string.
275    pub fn print_string(
276        &self,
277        format: SchemaOutputFormat,
278        options: SchemaPrinterFlags,
279    ) -> Result<String> {
280        let mut cstr = std::ptr::null_mut();
281        let cstr_ptr = &mut cstr;
282
283        let ret = unsafe {
284            ffi::lys_print_mem(
285                cstr_ptr,
286                self.raw,
287                format as u32,
288                options.bits(),
289            )
290        };
291        if ret != ffi::LY_ERR::LY_SUCCESS {
292            return Err(Error::new(self.context));
293        }
294
295        Ok(char_ptr_to_string(cstr))
296    }
297
298    /// Returns an iterator over the top-level data nodes.
299    pub fn data(&self) -> Siblings<'a, SchemaNode<'a>> {
300        let compiled = unsafe { (*self.raw).compiled };
301        let rdata = if compiled.is_null() {
302            std::ptr::null()
303        } else {
304            unsafe { (*compiled).data }
305        };
306        let data =
307            unsafe { SchemaNode::from_raw_opt(self.context, rdata as *mut _) };
308        Siblings::new(data)
309    }
310
311    /// Returns an iterator over the list of RPCs.
312    pub fn rpcs(&self) -> Siblings<'a, SchemaNode<'a>> {
313        let compiled = unsafe { (*self.raw).compiled };
314        let rdata = if compiled.is_null() {
315            std::ptr::null()
316        } else {
317            unsafe { (*compiled).rpcs }
318        };
319        let rpcs =
320            unsafe { SchemaNode::from_raw_opt(self.context, rdata as *mut _) };
321        Siblings::new(rpcs)
322    }
323
324    /// Returns an iterator over the list of notifications.
325    pub fn notifications(&self) -> Siblings<'a, SchemaNode<'a>> {
326        let compiled = unsafe { (*self.raw).compiled };
327        let rdata = if compiled.is_null() {
328            std::ptr::null()
329        } else {
330            unsafe { (*compiled).notifs }
331        };
332        let notifications =
333            unsafe { SchemaNode::from_raw_opt(self.context, rdata as *mut _) };
334        Siblings::new(notifications)
335    }
336
337    /// Returns an iterator over the list of extension instances.
338    pub fn extensions(&self) -> impl Iterator<Item = SchemaExtInstance<'a>> {
339        let compiled = unsafe { (*self.raw).compiled };
340        let array = unsafe { (*compiled).exts };
341        let ptr_size = mem::size_of::<ffi::lysc_ext_instance>();
342        Array::new(self.context, array as *mut _, ptr_size)
343    }
344
345    /// Returns an iterator over the top-level data nodes. The iteration
346    /// behavior is customizable using the provided `flags` option.
347    pub fn top_level_nodes(
348        &self,
349        flags: IterSchemaFlags,
350    ) -> impl Iterator<Item = SchemaNode<'a>> {
351        Getnext::new(flags, None, Some(self.clone()))
352    }
353
354    /// Returns an iterator over all data nodes in the schema module
355    /// (depth-first search algorithm).
356    ///
357    /// NOTE: augmentations (from other modules or from the module itself) are
358    /// also iterated over.
359    pub fn traverse(&self) -> impl Iterator<Item = SchemaNode<'a>> {
360        let data = self.data().flat_map(|snode| snode.traverse());
361        let rpcs = self.rpcs().flat_map(|snode| snode.traverse());
362        let notifications =
363            self.notifications().flat_map(|snode| snode.traverse());
364        data.chain(rpcs).chain(notifications)
365    }
366}
367
368unsafe impl<'a> Binding<'a> for SchemaModule<'a> {
369    type CType = ffi::lys_module;
370    type Container = Context;
371
372    unsafe fn from_raw(
373        context: &'a Context,
374        raw: *mut ffi::lys_module,
375    ) -> SchemaModule<'a> {
376        SchemaModule { context, raw }
377    }
378}
379
380impl PartialEq for SchemaModule<'_> {
381    fn eq(&self, other: &SchemaModule<'_>) -> bool {
382        self.raw == other.raw
383    }
384}
385
386unsafe impl Send for SchemaModule<'_> {}
387unsafe impl Sync for SchemaModule<'_> {}
388
389// ===== impl SchemaNode =====
390
391impl<'a> SchemaNode<'a> {
392    /// Returns a mutable raw pointer to the underlying C library representation
393    /// of the node.
394    pub fn as_raw(&self) -> *mut ffi::lysc_node {
395        self.raw
396    }
397
398    #[doc(hidden)]
399    fn check_flag(&self, flag: u32) -> bool {
400        let flags = unsafe { (*self.raw).flags } as u32;
401        flags & flag != 0
402    }
403
404    /// Schema node module.
405    pub fn module(&self) -> SchemaModule<'_> {
406        let module = unsafe { (*self.raw).module };
407        unsafe { SchemaModule::from_raw(self.context, module) }
408    }
409
410    /// Returns the kind of the schema node.
411    pub fn kind(&self) -> SchemaNodeKind {
412        self.kind
413    }
414
415    /// Schema node name.
416    pub fn name(&self) -> &str {
417        char_ptr_to_str(unsafe { (*self.raw).name })
418    }
419
420    /// Description statement.
421    pub fn description(&self) -> Option<&str> {
422        char_ptr_to_opt_str(unsafe { (*self.raw).dsc })
423    }
424
425    /// Reference statement.
426    pub fn reference(&self) -> Option<&str> {
427        char_ptr_to_opt_str(unsafe { (*self.raw).ref_ })
428    }
429
430    /// Generate path of the node.
431    pub fn path(&self, format: SchemaPathFormat) -> String {
432        let buf = std::mem::MaybeUninit::<[c_char; 4096]>::uninit();
433        let mut buf = unsafe { buf.assume_init() };
434
435        let ret = unsafe {
436            ffi::lysc_path(self.raw, format as u32, buf.as_mut_ptr(), buf.len())
437        };
438        if ret.is_null() {
439            panic!("Failed to generate path of the schema node");
440        }
441
442        char_ptr_to_string(buf.as_ptr())
443    }
444
445    /// Evaluate an xpath expression on the node.
446    pub fn find_xpath(&self, xpath: &str) -> Result<Set<'_, SchemaNode<'_>>> {
447        let xpath = CString::new(xpath).unwrap();
448        let mut set = std::ptr::null_mut();
449        let set_ptr = &mut set;
450        let options = 0u32;
451
452        let ret = unsafe {
453            ffi::lys_find_xpath(
454                std::ptr::null(),
455                self.raw,
456                xpath.as_ptr(),
457                options,
458                set_ptr,
459            )
460        };
461        if ret != ffi::LY_ERR::LY_SUCCESS {
462            return Err(Error::new(self.context));
463        }
464
465        let rnodes_count = unsafe { (*set).count } as usize;
466        let slice = if rnodes_count == 0 {
467            &[]
468        } else {
469            let rnodes = unsafe { (*set).__bindgen_anon_1.snodes };
470            unsafe { slice::from_raw_parts(rnodes, rnodes_count) }
471        };
472
473        Ok(Set::new(self.context, slice))
474    }
475
476    /// Get a schema node based on the given data path (JSON format).
477    pub fn find_path(&self, path: &str) -> Result<SchemaNode<'_>> {
478        let path = CString::new(path).unwrap();
479
480        let rnode = unsafe {
481            ffi::lys_find_path(std::ptr::null(), self.raw, path.as_ptr(), 0)
482        };
483        if rnode.is_null() {
484            return Err(Error::new(self.context));
485        }
486
487        Ok(unsafe { SchemaNode::from_raw(self.context, rnode as *mut _) })
488    }
489
490    /// Returns whether the node is a configuration node.
491    pub fn is_config(&self) -> bool {
492        match self.kind {
493            SchemaNodeKind::Container
494            | SchemaNodeKind::Case
495            | SchemaNodeKind::Choice
496            | SchemaNodeKind::Leaf
497            | SchemaNodeKind::LeafList
498            | SchemaNodeKind::List
499            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_CONFIG_W),
500            _ => false,
501        }
502    }
503
504    /// Returns whether the node is a state node.
505    pub fn is_state(&self) -> bool {
506        match self.kind {
507            SchemaNodeKind::Container
508            | SchemaNodeKind::Case
509            | SchemaNodeKind::Choice
510            | SchemaNodeKind::Leaf
511            | SchemaNodeKind::LeafList
512            | SchemaNodeKind::List
513            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_CONFIG_R),
514            _ => false,
515        }
516    }
517
518    /// Returns whether the node's status is "current".
519    pub fn is_status_current(&self) -> bool {
520        self.check_flag(ffi::LYS_STATUS_CURR)
521    }
522
523    /// Returns whether the node's status is "deprecated".
524    pub fn is_status_deprecated(&self) -> bool {
525        self.check_flag(ffi::LYS_STATUS_DEPRC)
526    }
527
528    /// Returns whether the node's status is "obsolete".
529    pub fn is_status_obsolete(&self) -> bool {
530        self.check_flag(ffi::LYS_STATUS_OBSLT)
531    }
532
533    /// Returns whether the node is mandatory.
534    pub fn is_mandatory(&self) -> bool {
535        match self.kind {
536            SchemaNodeKind::Container
537            | SchemaNodeKind::Choice
538            | SchemaNodeKind::Leaf
539            | SchemaNodeKind::LeafList
540            | SchemaNodeKind::List
541            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_MAND_TRUE),
542            _ => false,
543        }
544    }
545
546    /// Returns whether the node is a non-presence container.
547    pub fn is_np_container(&self) -> bool {
548        match self.kind {
549            SchemaNodeKind::Container => !self.check_flag(ffi::LYS_PRESENCE),
550            _ => false,
551        }
552    }
553
554    /// Returns whether the node is a list's key.
555    pub fn is_list_key(&self) -> bool {
556        match self.kind {
557            SchemaNodeKind::Leaf => self.check_flag(ffi::LYS_KEY),
558            _ => false,
559        }
560    }
561
562    /// Returns whether the node is a keyless list.
563    pub fn is_keyless_list(&self) -> bool {
564        match self.kind {
565            SchemaNodeKind::List => self.check_flag(ffi::LYS_KEYLESS),
566            _ => false,
567        }
568    }
569
570    /// Returns whether the node is an user-ordered list or leaf-list.
571    pub fn is_user_ordered(&self) -> bool {
572        match self.kind {
573            SchemaNodeKind::LeafList | SchemaNodeKind::List => {
574                self.check_flag(ffi::LYS_ORDBY_USER)
575            }
576            _ => false,
577        }
578    }
579
580    /// Returns whether the node appears only in the schema tree and not in the
581    /// data tree.
582    pub fn is_schema_only(&self) -> bool {
583        matches!(self.kind(), SchemaNodeKind::Choice | SchemaNodeKind::Case)
584    }
585
586    /// Returns whether the node is in the subtree of an input statement.
587    pub fn is_within_input(&self) -> bool {
588        match self.kind {
589            SchemaNodeKind::Container
590            | SchemaNodeKind::Case
591            | SchemaNodeKind::Choice
592            | SchemaNodeKind::Leaf
593            | SchemaNodeKind::LeafList
594            | SchemaNodeKind::List
595            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_IS_INPUT),
596            _ => false,
597        }
598    }
599
600    /// Returns whether the node is in the subtree of an output statement.
601    pub fn is_within_output(&self) -> bool {
602        match self.kind {
603            SchemaNodeKind::Container
604            | SchemaNodeKind::Case
605            | SchemaNodeKind::Choice
606            | SchemaNodeKind::Leaf
607            | SchemaNodeKind::LeafList
608            | SchemaNodeKind::List
609            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_IS_OUTPUT),
610            _ => false,
611        }
612    }
613
614    /// Returns whether the node is in the subtree of a notification statement.
615    pub fn is_within_notification(&self) -> bool {
616        match self.kind {
617            SchemaNodeKind::Container
618            | SchemaNodeKind::Case
619            | SchemaNodeKind::Choice
620            | SchemaNodeKind::Leaf
621            | SchemaNodeKind::LeafList
622            | SchemaNodeKind::List
623            | SchemaNodeKind::AnyData => self.check_flag(ffi::LYS_IS_NOTIF),
624            _ => false,
625        }
626    }
627
628    /// Returns whether a default value is set.
629    pub fn has_default(&self) -> bool {
630        match self.kind {
631            SchemaNodeKind::Case
632            | SchemaNodeKind::Leaf
633            | SchemaNodeKind::LeafList => self.check_flag(ffi::LYS_SET_DFLT),
634            _ => false,
635        }
636    }
637
638    /// The default value of the leaf (canonical string representation).
639    pub fn default_value_canonical(&self) -> Option<&str> {
640        let default = unsafe {
641            match self.kind() {
642                SchemaNodeKind::Leaf => {
643                    let rvalue =
644                        (*(self.raw as *const ffi::lysc_node_leaf)).dflt;
645                    let mut canonical = (*rvalue)._canonical;
646                    if canonical.is_null() {
647                        canonical = ffi::lyd_value_get_canonical(
648                            self.context.raw,
649                            rvalue,
650                        )
651                    }
652                    canonical
653                }
654                _ => return None,
655            }
656        };
657
658        char_ptr_to_opt_str(default)
659    }
660
661    /// The default value of the leaf (typed representation).
662    pub fn default_value(&self) -> Option<DataValue> {
663        match self.kind() {
664            SchemaNodeKind::Leaf => {
665                let default = unsafe {
666                    let rvalue =
667                        (*(self.raw as *const ffi::lysc_node_leaf)).dflt;
668                    if rvalue.is_null() {
669                        return None;
670                    }
671                    DataValue::from_raw(self.context, rvalue)
672                };
673                Some(default)
674            }
675            _ => None,
676        }
677    }
678
679    /// The default case of the choice.
680    pub fn default_case(&self) -> Option<SchemaNode<'_>> {
681        let default = unsafe {
682            match self.kind() {
683                SchemaNodeKind::Choice => {
684                    (*(self.raw as *mut ffi::lysc_node_choice)).dflt
685                }
686                _ => return None,
687            }
688        };
689
690        unsafe { SchemaNode::from_raw_opt(self.context, default as *mut _) }
691    }
692
693    // TODO: list of leaf-list default values.
694
695    /// Type of the leaf(-list) node.
696    pub fn leaf_type(&self) -> Option<SchemaLeafType<'_>> {
697        let raw = unsafe {
698            match self.kind() {
699                SchemaNodeKind::Leaf => {
700                    (*(self.raw as *mut ffi::lysc_node_leaf)).type_
701                }
702                SchemaNodeKind::LeafList => {
703                    (*(self.raw as *mut ffi::lysc_node_leaflist)).type_
704                }
705                _ => return None,
706            }
707        };
708        let ltype = unsafe { SchemaLeafType::from_raw(self.context, raw) };
709        Some(ltype)
710    }
711
712    /// Units of the leaf(-list)'s type.
713    pub fn units(&self) -> Option<&str> {
714        let units = unsafe {
715            match self.kind() {
716                SchemaNodeKind::Leaf => {
717                    (*(self.raw as *mut ffi::lysc_node_leaf)).units
718                }
719                SchemaNodeKind::LeafList => {
720                    (*(self.raw as *mut ffi::lysc_node_leaflist)).units
721                }
722                _ => return None,
723            }
724        };
725
726        char_ptr_to_opt_str(units)
727    }
728
729    /// The min-elements constraint.
730    pub fn min_elements(&self) -> Option<u32> {
731        let min = unsafe {
732            match self.kind() {
733                SchemaNodeKind::LeafList => {
734                    (*(self.raw as *mut ffi::lysc_node_leaflist)).min
735                }
736                SchemaNodeKind::List => {
737                    (*(self.raw as *mut ffi::lysc_node_list)).min
738                }
739                _ => return None,
740            }
741        };
742
743        if min != 0 {
744            Some(min)
745        } else {
746            None
747        }
748    }
749
750    /// The max-elements constraint.
751    pub fn max_elements(&self) -> Option<u32> {
752        let max = unsafe {
753            match self.kind() {
754                SchemaNodeKind::LeafList => {
755                    (*(self.raw as *mut ffi::lysc_node_leaflist)).max
756                }
757                SchemaNodeKind::List => {
758                    (*(self.raw as *mut ffi::lysc_node_list)).max
759                }
760                _ => return None,
761            }
762        };
763        if max != u32::MAX {
764            Some(max)
765        } else {
766            None
767        }
768    }
769
770    /// Array of must restrictions.
771    pub fn musts(&self) -> Option<Array<'_, SchemaStmtMust<'_>>> {
772        let array = unsafe { ffi::lysc_node_musts(self.raw) };
773        let ptr_size = mem::size_of::<ffi::lysc_must>();
774        Some(Array::new(self.context, array as *mut _, ptr_size))
775    }
776
777    /// Array of when statements.
778    pub fn whens(&self) -> Array<'_, SchemaStmtWhen<'_>> {
779        let array = unsafe { ffi::lysc_node_when(self.raw) };
780        let ptr_size = mem::size_of::<ffi::lysc_when>();
781        Array::new(self.context, array as *mut _, ptr_size)
782    }
783
784    /// Array of actions.
785    pub fn actions(&self) -> impl Iterator<Item = SchemaNode<'a>> + 'a {
786        let rnode = unsafe {
787            match self.kind {
788                SchemaNodeKind::Container => {
789                    (*(self.raw as *mut ffi::lysc_node_container)).actions
790                }
791                SchemaNodeKind::List => {
792                    (*(self.raw as *mut ffi::lysc_node_list)).actions
793                }
794                _ => std::ptr::null_mut(),
795            }
796        };
797
798        let node =
799            unsafe { SchemaNode::from_raw_opt(self.context, rnode as *mut _) };
800        Siblings::new(node)
801    }
802
803    /// Array of notifications.
804    pub fn notifications(&self) -> impl Iterator<Item = SchemaNode<'a>> + 'a {
805        let rnode = unsafe {
806            match self.kind {
807                SchemaNodeKind::Container => {
808                    (*(self.raw as *mut ffi::lysc_node_container)).notifs
809                }
810                SchemaNodeKind::List => {
811                    (*(self.raw as *mut ffi::lysc_node_list)).notifs
812                }
813                _ => std::ptr::null_mut(),
814            }
815        };
816
817        let node =
818            unsafe { SchemaNode::from_raw_opt(self.context, rnode as *mut _) };
819        Siblings::new(node)
820    }
821
822    /// RPC's input. Returns a tuple containing the following:
823    /// * Iterator over the input child nodes;
824    /// * Input's list of must restrictions.
825    pub fn input(
826        &self,
827    ) -> Option<(Siblings<'_, SchemaNode<'_>>, Array<'_, SchemaStmtMust<'_>>)>
828    {
829        match self.kind {
830            SchemaNodeKind::Rpc | SchemaNodeKind::Action => {
831                let raw = self.raw as *mut ffi::lysc_node_action;
832                let input = unsafe { (*raw).input };
833                let rnode = input.child;
834                let rmusts = input.musts;
835
836                let node =
837                    unsafe { SchemaNode::from_raw_opt(self.context, rnode) };
838                let nodes = Siblings::new(node);
839                let ptr_size = mem::size_of::<ffi::lysc_must>();
840                let musts = Array::new(self.context, rmusts, ptr_size);
841                Some((nodes, musts))
842            }
843            _ => None,
844        }
845    }
846
847    /// RPC's output. Returns a tuple containing the following:
848    /// * Iterator over the output child nodes;
849    /// * Output's list of must restrictions.
850    pub fn output(
851        &self,
852    ) -> Option<(Siblings<'_, SchemaNode<'_>>, Array<'_, SchemaStmtMust<'_>>)>
853    {
854        match self.kind {
855            SchemaNodeKind::Rpc | SchemaNodeKind::Action => {
856                let raw = self.raw as *mut ffi::lysc_node_action;
857                let output = unsafe { (*raw).output };
858                let rnode = output.child;
859                let rmusts = output.musts;
860
861                let node =
862                    unsafe { SchemaNode::from_raw_opt(self.context, rnode) };
863                let nodes = Siblings::new(node);
864                let ptr_size = mem::size_of::<ffi::lysc_must>();
865                let musts = Array::new(self.context, rmusts, ptr_size);
866                Some((nodes, musts))
867            }
868            _ => None,
869        }
870    }
871
872    /// Returns an iterator over the ancestor schema nodes.
873    pub fn ancestors(&self) -> Ancestors<'a, SchemaNode<'a>> {
874        let parent = self.parent();
875        Ancestors::new(parent)
876    }
877
878    /// Returns an iterator over this schema node and its ancestors.
879    pub fn inclusive_ancestors(&self) -> Ancestors<'a, SchemaNode<'a>> {
880        Ancestors::new(Some(self.clone()))
881    }
882
883    /// Returns an iterator over the sibling schema nodes.
884    pub fn siblings(&self) -> Siblings<'a, SchemaNode<'a>> {
885        let sibling = self.next_sibling();
886        Siblings::new(sibling)
887    }
888
889    /// Returns an iterator over this schema node and its siblings.
890    pub fn inclusive_siblings(&self) -> Siblings<'a, SchemaNode<'a>> {
891        Siblings::new(Some(self.clone()))
892    }
893
894    /// Returns an iterator over the child schema nodes, excluding action and
895    /// notification nodes.
896    pub fn children(&self) -> Siblings<'a, SchemaNode<'a>> {
897        let child = self.first_child();
898        Siblings::new(child)
899    }
900
901    /// Returns an iterator over all child schema nodes, including action and
902    /// notification nodes.
903    pub fn all_children(&self) -> impl Iterator<Item = SchemaNode<'a>> {
904        let child = self.first_child();
905        Siblings::new(child)
906            .chain(self.actions())
907            .chain(self.notifications())
908    }
909
910    /// Returns an iterator over all child schema nodes. The iteration behavior
911    /// is customizable using the provided `flags` option.
912    pub fn children2(
913        &self,
914        flags: IterSchemaFlags,
915    ) -> impl Iterator<Item = SchemaNode<'a>> {
916        Getnext::new(flags, Some(self.clone()), None)
917    }
918
919    /// Returns an iterator over all elements in the schema tree (depth-first
920    /// search algorithm).
921    pub fn traverse(&self) -> Traverse<'a, SchemaNode<'a>> {
922        Traverse::new(self.clone())
923    }
924
925    /// Returns an iterator over the keys of the list.
926    pub fn list_keys(&self) -> impl Iterator<Item = SchemaNode<'a>> {
927        self.children().filter(|snode| snode.is_list_key())
928    }
929
930    /// Set a schema private pointer to a user pointer.
931    ///
932    /// # Safety
933    ///
934    /// The caller must ensure that the provided pointer is valid.
935    pub unsafe fn set_private(&self, ptr: *mut c_void) {
936        (*self.raw).priv_ = ptr;
937    }
938
939    /// Get private user data, not used by libyang.
940    pub fn get_private(&self) -> Option<*mut c_void> {
941        let priv_ = unsafe { (*self.raw).priv_ };
942        if priv_.is_null() {
943            None
944        } else {
945            Some(priv_)
946        }
947    }
948}
949
950unsafe impl<'a> Binding<'a> for SchemaNode<'a> {
951    type CType = ffi::lysc_node;
952    type Container = Context;
953
954    unsafe fn from_raw(
955        context: &'a Context,
956        raw: *mut ffi::lysc_node,
957    ) -> SchemaNode<'a> {
958        let nodetype = unsafe { (*raw).nodetype } as u32;
959        let kind = match nodetype {
960            ffi::LYS_CONTAINER => SchemaNodeKind::Container,
961            ffi::LYS_CASE => SchemaNodeKind::Case,
962            ffi::LYS_CHOICE => SchemaNodeKind::Choice,
963            ffi::LYS_LEAF => SchemaNodeKind::Leaf,
964            ffi::LYS_LEAFLIST => SchemaNodeKind::LeafList,
965            ffi::LYS_LIST => SchemaNodeKind::List,
966            ffi::LYS_ANYDATA => SchemaNodeKind::AnyData,
967            ffi::LYS_ACTION => SchemaNodeKind::Action,
968            ffi::LYS_RPC => SchemaNodeKind::Rpc,
969            ffi::LYS_INPUT => SchemaNodeKind::Input,
970            ffi::LYS_OUTPUT => SchemaNodeKind::Output,
971            ffi::LYS_NOTIF => SchemaNodeKind::Notification,
972            _ => panic!("unknown node type"),
973        };
974        SchemaNode { context, raw, kind }
975    }
976}
977
978impl<'a> NodeIterable<'a> for SchemaNode<'a> {
979    fn parent(&self) -> Option<SchemaNode<'a>> {
980        let rparent = unsafe { (*self.raw).parent };
981        unsafe { SchemaNode::from_raw_opt(self.context, rparent) }
982    }
983
984    fn next_sibling(&self) -> Option<SchemaNode<'a>> {
985        let rnext = unsafe { (*self.raw).next };
986        unsafe { SchemaNode::from_raw_opt(self.context, rnext) }
987    }
988
989    fn first_child(&self) -> Option<SchemaNode<'a>> {
990        let rchild = unsafe { ffi::lysc_node_child(&*self.raw) };
991        unsafe { SchemaNode::from_raw_opt(self.context, rchild as *mut _) }
992    }
993}
994
995impl PartialEq for SchemaNode<'_> {
996    fn eq(&self, other: &SchemaNode<'_>) -> bool {
997        self.raw == other.raw
998    }
999}
1000
1001unsafe impl Send for SchemaNode<'_> {}
1002unsafe impl Sync for SchemaNode<'_> {}
1003
1004// ===== impl SchemaStmtMust =====
1005
1006impl SchemaStmtMust<'_> {
1007    // TODO: XPath condition
1008
1009    /// Returns a mutable raw pointer to the underlying C library representation
1010    /// of the must statement.
1011    pub fn as_raw(&self) -> *mut ffi::lysc_must {
1012        self.raw
1013    }
1014
1015    /// description substatement.
1016    pub fn description(&self) -> Option<&str> {
1017        char_ptr_to_opt_str(unsafe { (*self.raw).dsc })
1018    }
1019
1020    /// reference substatement.
1021    pub fn reference(&self) -> Option<&str> {
1022        char_ptr_to_opt_str(unsafe { (*self.raw).ref_ })
1023    }
1024
1025    /// error-message substatement.
1026    pub fn error_msg(&self) -> Option<&str> {
1027        char_ptr_to_opt_str(unsafe { (*self.raw).emsg })
1028    }
1029
1030    /// error-app-tag substatement.
1031    pub fn error_apptag(&self) -> Option<&str> {
1032        char_ptr_to_opt_str(unsafe { (*self.raw).eapptag })
1033    }
1034}
1035
1036unsafe impl<'a> Binding<'a> for SchemaStmtMust<'a> {
1037    type CType = ffi::lysc_must;
1038    type Container = Context;
1039
1040    unsafe fn from_raw(
1041        _context: &'a Context,
1042        raw: *mut ffi::lysc_must,
1043    ) -> SchemaStmtMust<'a> {
1044        SchemaStmtMust {
1045            raw,
1046            _marker: std::marker::PhantomData,
1047        }
1048    }
1049}
1050
1051unsafe impl Send for SchemaStmtMust<'_> {}
1052unsafe impl Sync for SchemaStmtMust<'_> {}
1053
1054// ===== impl SchemaStmtWhen =====
1055
1056impl SchemaStmtWhen<'_> {
1057    // TODO: XPath condition
1058
1059    /// Returns a mutable raw pointer to the underlying C library representation
1060    /// of the when statement.
1061    pub fn as_raw(&self) -> *mut ffi::lysc_when {
1062        self.raw
1063    }
1064
1065    /// description substatement.
1066    pub fn description(&self) -> Option<&str> {
1067        char_ptr_to_opt_str(unsafe { (*self.raw).dsc })
1068    }
1069
1070    /// reference substatement.
1071    pub fn reference(&self) -> Option<&str> {
1072        char_ptr_to_opt_str(unsafe { (*self.raw).ref_ })
1073    }
1074}
1075
1076unsafe impl<'a> Binding<'a> for SchemaStmtWhen<'a> {
1077    type CType = *mut ffi::lysc_when;
1078    type Container = Context;
1079
1080    unsafe fn from_raw(
1081        _context: &'a Context,
1082        raw: *mut *mut ffi::lysc_when,
1083    ) -> SchemaStmtWhen<'a> {
1084        let raw = unsafe { *raw };
1085        SchemaStmtWhen {
1086            raw,
1087            _marker: std::marker::PhantomData,
1088        }
1089    }
1090}
1091
1092unsafe impl Send for SchemaStmtWhen<'_> {}
1093unsafe impl Sync for SchemaStmtWhen<'_> {}
1094
1095// ===== impl SchemaLeafType =====
1096
1097impl SchemaLeafType<'_> {
1098    /// Returns a mutable raw pointer to the underlying C library representation
1099    /// of the leaf type.
1100    pub fn as_raw(&self) -> *mut ffi::lysc_type {
1101        self.raw
1102    }
1103
1104    /// Returns the resolved base type.
1105    pub fn base_type(&self) -> DataValueType {
1106        let base_type = unsafe { (*self.raw).basetype };
1107        DataValueType::from_u32(base_type).unwrap()
1108    }
1109
1110    /// Returns the typedef name if it exists.
1111    pub fn typedef_name(&self) -> Option<String> {
1112        let typedef = unsafe { (*self.raw).name };
1113        char_ptr_to_opt_string(typedef)
1114    }
1115
1116    /// Returns the real type of the leafref, corresponding to the first
1117    /// non-leafref in a possible chain of leafrefs.
1118    pub fn leafref_real_type(&self) -> Option<SchemaLeafType<'_>> {
1119        if self.base_type() != DataValueType::LeafRef {
1120            return None;
1121        }
1122
1123        let leafref = self.raw as *mut ffi::lysc_type_leafref;
1124        let real_type = unsafe { (*leafref).realtype };
1125        let ltype =
1126            unsafe { SchemaLeafType::from_raw(self.context, real_type) };
1127        Some(ltype)
1128    }
1129}
1130
1131unsafe impl<'a> Binding<'a> for SchemaLeafType<'a> {
1132    type CType = ffi::lysc_type;
1133    type Container = Context;
1134
1135    unsafe fn from_raw(
1136        context: &'a Context,
1137        raw: *mut ffi::lysc_type,
1138    ) -> SchemaLeafType<'a> {
1139        SchemaLeafType { context, raw }
1140    }
1141}
1142
1143unsafe impl Send for SchemaLeafType<'_> {}
1144unsafe impl Sync for SchemaLeafType<'_> {}
1145
1146// ===== impl SchemaExtInstance =====
1147
1148impl<'a> SchemaExtInstance<'a> {
1149    /// Returns a mutable raw pointer to the underlying C library representation
1150    /// of the extension instance.
1151    pub fn as_raw(&self) -> *mut ffi::lysc_ext_instance {
1152        self.raw
1153    }
1154
1155    /// Returns the optional extension's argument.
1156    pub fn argument(&self) -> Option<String> {
1157        let argument = unsafe { (*self.raw).argument };
1158        char_ptr_to_opt_string(argument)
1159    }
1160
1161    /// Create a new node in the extension instance based on a path.
1162    ///
1163    /// If path points to a list key and the list instance does not exist,
1164    /// the key value from the predicate is used and value is ignored. Also,
1165    /// if a leaf-list is being created and both a predicate is defined in
1166    /// path and value is set, the predicate is preferred.
1167    ///
1168    /// For key-less lists and state leaf-lists, positional predicates can be
1169    /// used. If no preciate is used for these nodes, they are always created.
1170    ///
1171    /// The output parameter can be used to change the behavior to ignore
1172    /// RPC/action input schema nodes and use only output ones.
1173    ///
1174    /// Returns the last created node (if any).
1175    pub fn new_path(
1176        &self,
1177        path: &str,
1178        value: Option<&str>,
1179        output: bool,
1180    ) -> Result<Option<DataTree<'a>>> {
1181        let path = CString::new(path).unwrap();
1182        let mut rnode = std::ptr::null_mut();
1183        let rnode_ptr = &mut rnode;
1184        let value_cstr;
1185
1186        let value_ptr = match value {
1187            Some(value) => {
1188                value_cstr = CString::new(value).unwrap();
1189                value_cstr.as_ptr()
1190            }
1191            None => std::ptr::null(),
1192        };
1193
1194        let mut options = ffi::LYD_NEW_PATH_UPDATE;
1195        if output {
1196            options |= ffi::LYD_NEW_VAL_OUTPUT;
1197        }
1198
1199        let ret = unsafe {
1200            ffi::lyd_new_ext_path(
1201                std::ptr::null_mut(),
1202                self.raw,
1203                path.as_ptr(),
1204                value_ptr as *const c_void,
1205                options,
1206                rnode_ptr,
1207            )
1208        };
1209        if ret != ffi::LY_ERR::LY_SUCCESS {
1210            return Err(Error::new(self.context));
1211        }
1212
1213        Ok(unsafe { DataTree::from_raw_opt(self.context, rnode) })
1214    }
1215
1216    /// Create a new top-level inner node (container, notification, RPC or
1217    /// action) in the in the extension instance.
1218    ///
1219    /// Returns the created node.
1220    pub fn new_inner(&self, name: &str) -> Result<DataTree<'a>> {
1221        let name_cstr = CString::new(name).unwrap();
1222        let mut rnode = std::ptr::null_mut();
1223        let rnode_ptr = &mut rnode;
1224
1225        let ret = unsafe {
1226            ffi::lyd_new_ext_inner(self.raw, name_cstr.as_ptr(), rnode_ptr)
1227        };
1228        if ret != ffi::LY_ERR::LY_SUCCESS {
1229            return Err(Error::new(self.context));
1230        }
1231
1232        Ok(unsafe { DataTree::from_raw(self.context, rnode) })
1233    }
1234}
1235
1236unsafe impl<'a> Binding<'a> for SchemaExtInstance<'a> {
1237    type CType = ffi::lysc_ext_instance;
1238    type Container = Context;
1239
1240    unsafe fn from_raw(
1241        context: &'a Context,
1242        raw: *mut ffi::lysc_ext_instance,
1243    ) -> SchemaExtInstance<'a> {
1244        SchemaExtInstance { context, raw }
1245    }
1246}
1247
1248unsafe impl Send for SchemaExtInstance<'_> {}
1249unsafe impl Sync for SchemaExtInstance<'_> {}
1250
1251// ===== impl DataValue =====
1252
1253impl DataValue {
1254    pub(crate) unsafe fn from_raw(
1255        context: &Context,
1256        raw: *const ffi::lyd_value,
1257    ) -> DataValue {
1258        let rtype = (*(*raw).realtype).basetype;
1259        match rtype {
1260            ffi::LY_DATA_TYPE::LY_TYPE_UINT8 => {
1261                let value = (*raw).__bindgen_anon_1.uint8;
1262                DataValue::Uint8(value)
1263            }
1264            ffi::LY_DATA_TYPE::LY_TYPE_UINT16 => {
1265                let value = (*raw).__bindgen_anon_1.uint16;
1266                DataValue::Uint16(value)
1267            }
1268            ffi::LY_DATA_TYPE::LY_TYPE_UINT32 => {
1269                let value = (*raw).__bindgen_anon_1.uint32;
1270                DataValue::Uint32(value)
1271            }
1272            ffi::LY_DATA_TYPE::LY_TYPE_UINT64 => {
1273                let value = (*raw).__bindgen_anon_1.uint64;
1274                DataValue::Uint64(value)
1275            }
1276            ffi::LY_DATA_TYPE::LY_TYPE_BOOL => {
1277                let value = (*raw).__bindgen_anon_1.boolean != 0;
1278                DataValue::Bool(value)
1279            }
1280            ffi::LY_DATA_TYPE::LY_TYPE_INT8 => {
1281                let value = (*raw).__bindgen_anon_1.int8;
1282                DataValue::Int8(value)
1283            }
1284            ffi::LY_DATA_TYPE::LY_TYPE_INT16 => {
1285                let value = (*raw).__bindgen_anon_1.int16;
1286                DataValue::Int16(value)
1287            }
1288            ffi::LY_DATA_TYPE::LY_TYPE_INT32 => {
1289                let value = (*raw).__bindgen_anon_1.int32;
1290                DataValue::Int32(value)
1291            }
1292            ffi::LY_DATA_TYPE::LY_TYPE_INT64 => {
1293                let value = (*raw).__bindgen_anon_1.int64;
1294                DataValue::Int64(value)
1295            }
1296            _ => {
1297                let mut canonical = (*raw)._canonical;
1298                if canonical.is_null() {
1299                    canonical = ffi::lyd_value_get_canonical(context.raw, raw);
1300                }
1301                DataValue::Other(char_ptr_to_string(canonical))
1302            }
1303        }
1304    }
1305}