Skip to main content

mib_rs/mib/
types.rs

1//! Shared types used across the resolved MIB model.
2//!
3//! Contains arena id newtypes ([`NodeId`], [`ObjectId`], [`TypeId`], etc.),
4//! supporting data structures ([`Range`], [`NamedValue`], [`DefVal`],
5//! [`IndexEntry`]), and SMI clause representations used by compliance and
6//! capability definitions.
7
8use std::fmt;
9
10use crate::mib::Oid;
11use crate::source::SourceRange;
12use crate::types::{Access, BaseType, IndexEncoding};
13
14/// A single imported symbol with its source location.
15///
16/// Part of an [`Import`] group. The `name` is the symbol as written in the
17/// MIB's IMPORTS clause (e.g. `"ifIndex"`, `"DisplayString"`).
18#[derive(Debug, Clone)]
19pub struct ImportSymbol {
20    /// The symbol name as it appears in the IMPORTS clause.
21    pub name: String,
22    /// Source location of this symbol reference.
23    pub range: SourceRange,
24}
25
26/// A group of symbols imported from a single source module.
27///
28/// Each MIB module's IMPORTS section is represented as a list of `Import`
29/// entries, one per source module.
30#[derive(Debug, Clone)]
31pub struct Import {
32    /// Name of the module being imported from.
33    pub module: String,
34    /// Symbols imported from this module.
35    pub symbols: Vec<ImportSymbol>,
36}
37
38/// Resolver strategy used for one imported symbol.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ImportResolutionMode {
41    /// The selected source module directly defines the complete import group.
42    Direct,
43    /// A well-known source-module alias selected the defining module.
44    Alias,
45    /// The declared source module forwarded the complete import group.
46    Forwarded,
47    /// The symbol was retained while resolving a mixed direct/forwarded group.
48    Partial,
49    /// No source module candidate could resolve the symbol.
50    Unresolved,
51    /// No target was selected and at least one candidate path was cyclic.
52    Cycle,
53}
54
55impl fmt::Display for ImportResolutionMode {
56    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57        formatter.write_str(match self {
58            Self::Direct => "direct",
59            Self::Alias => "alias",
60            Self::Forwarded => "forwarded",
61            Self::Partial => "partial",
62            Self::Unresolved => "unresolved",
63            Self::Cycle => "cycle",
64        })
65    }
66}
67
68/// Terminal result of one candidate path attempted during import resolution.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum ImportAttemptOutcome {
71    /// The path reached a module defining the symbol.
72    Resolved,
73    /// The path ended at a loaded module that neither defines nor imports the symbol.
74    SymbolNotDefined,
75    /// The next declared source module was unavailable.
76    ModuleNotFound,
77    /// The path revisited a module.
78    Cycle,
79}
80
81/// Import resolver stage that produced a candidate-path attempt.
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ImportResolutionStage {
84    /// Aggregate direct-source candidate scoring.
85    Direct,
86    /// Aggregate well-known module-alias candidate scoring.
87    Alias,
88    /// Aggregate import-forwarding traversal.
89    Forwarding,
90    /// Per-symbol partial-resolution traversal.
91    Partial,
92    /// Terminal failure because the declared source module was unavailable.
93    Unresolved,
94}
95
96impl fmt::Display for ImportResolutionStage {
97    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98        formatter.write_str(match self {
99            Self::Direct => "direct",
100            Self::Alias => "alias",
101            Self::Forwarding => "forwarding",
102            Self::Partial => "partial",
103            Self::Unresolved => "unresolved",
104        })
105    }
106}
107
108impl fmt::Display for ImportAttemptOutcome {
109    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
110        formatter.write_str(match self {
111            Self::Resolved => "resolved",
112            Self::SymbolNotDefined => "symbol-not-defined",
113            Self::ModuleNotFound => "module-not-found",
114            Self::Cycle => "cycle",
115        })
116    }
117}
118
119/// One exact module-version path observed during live import resolution.
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct ImportResolutionAttempt {
122    /// Live resolver stage that executed this attempt.
123    pub stage: ImportResolutionStage,
124    /// Loaded module versions visited by the attempt, including a repeated
125    /// final module for a detected cycle.
126    pub path: Vec<ModuleId>,
127    /// Unavailable module named by the last visited module, when applicable.
128    pub missing_module: Option<String>,
129    /// Terminal result.
130    pub outcome: ImportAttemptOutcome,
131    /// Whether this attempt supplied the retained resolution.
132    pub selected: bool,
133}
134
135/// Retained pre-collapse provenance for one IMPORTS symbol.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct ImportResolution {
138    /// Imported symbol.
139    pub symbol: String,
140    /// Source module name written in the importing module.
141    pub declared_module: String,
142    /// Resolver strategy used for the import group.
143    pub mode: ImportResolutionMode,
144    /// Exact defining module version retained after resolution.
145    pub target: Option<ModuleId>,
146    /// Exact selected forwarding path, excluding the importing module itself.
147    pub selected_path: Vec<ModuleId>,
148    /// Candidate paths attempted before the transitive import map was collapsed.
149    pub attempts: Vec<ImportResolutionAttempt>,
150}
151
152/// An endpoint in a resolved SIZE or value range constraint.
153///
154/// Signed and unsigned literals remain distinct so values above [`i64::MAX`]
155/// are represented exactly. `MIN` and `MAX` are retained when no parent or
156/// base-type bound is available to give them a concrete value. Malformed or
157/// unsupported literals retain their source text in [`Raw`](Self::Raw).
158///
159/// The raw representation means this enum is no longer `Copy`; clone endpoints
160/// when an owned value is required.
161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
162pub enum RangeBound {
163    /// A signed integer literal.
164    Signed(i64),
165    /// An unsigned integer literal.
166    Unsigned(u64),
167    /// The `MIN` keyword.
168    Min,
169    /// The `MAX` keyword.
170    Max,
171    /// An unresolved endpoint preserving its source text.
172    Raw(String),
173}
174
175impl RangeBound {
176    /// Return the endpoint as `i64` when it is concrete and representable.
177    pub fn as_i64(&self) -> Option<i64> {
178        match self {
179            Self::Signed(value) => Some(*value),
180            Self::Unsigned(value) => i64::try_from(*value).ok(),
181            Self::Min | Self::Max | Self::Raw(_) => None,
182        }
183    }
184
185    /// Return the endpoint as `u64` when it is concrete and non-negative.
186    pub fn as_u64(&self) -> Option<u64> {
187        match self {
188            Self::Signed(value) => u64::try_from(*value).ok(),
189            Self::Unsigned(value) => Some(*value),
190            Self::Min | Self::Max | Self::Raw(_) => None,
191        }
192    }
193
194    /// Return whether the endpoint is a signed or unsigned number.
195    pub fn is_concrete(&self) -> bool {
196        matches!(self, Self::Signed(_) | Self::Unsigned(_))
197    }
198
199    pub(crate) fn cmp_value(&self, other: &Self) -> Option<std::cmp::Ordering> {
200        use std::cmp::Ordering;
201        match (self, other) {
202            (Self::Raw(_), _) | (_, Self::Raw(_)) => None,
203            (Self::Min, Self::Min) | (Self::Max, Self::Max) => Some(Ordering::Equal),
204            (Self::Min, _) | (_, Self::Max) => Some(Ordering::Less),
205            (Self::Max, _) | (_, Self::Min) => Some(Ordering::Greater),
206            (Self::Signed(left), Self::Signed(right)) => Some(left.cmp(right)),
207            (Self::Unsigned(left), Self::Unsigned(right)) => Some(left.cmp(right)),
208            (Self::Signed(left), Self::Unsigned(right)) => Some(if *left < 0 {
209                Ordering::Less
210            } else {
211                (*left as u64).cmp(right)
212            }),
213            (Self::Unsigned(left), Self::Signed(right)) => Some(if *right < 0 {
214                Ordering::Greater
215            } else {
216                left.cmp(&(*right as u64))
217            }),
218        }
219    }
220}
221
222impl fmt::Display for RangeBound {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        match self {
225            Self::Signed(value) => write!(f, "{value}"),
226            Self::Unsigned(value) => write!(f, "{value}"),
227            Self::Min => f.write_str("MIN"),
228            Self::Max => f.write_str("MAX"),
229            Self::Raw(value) => f.write_str(value),
230        }
231    }
232}
233
234/// A min..max constraint range, used for both SIZE and value constraints.
235///
236/// For single-value constraints (e.g. `SIZE (6)`), `min` equals `max`.
237#[derive(Debug, Clone, PartialEq, Eq)]
238pub struct Range {
239    /// Lower bound (inclusive).
240    pub min: RangeBound,
241    /// Upper bound (inclusive). Equal to `min` for single-value ranges.
242    pub max: RangeBound,
243    /// Source location of this constraint.
244    pub range: Option<SourceRange>,
245}
246
247impl Range {
248    /// Return whether both endpoints are concrete numeric values.
249    pub fn is_resolved(&self) -> bool {
250        self.min.is_concrete() && self.max.is_concrete()
251    }
252
253    pub(crate) fn contains_i64(&self, value: i64) -> bool {
254        let value = RangeBound::Signed(value);
255        self.min
256            .cmp_value(&value)
257            .is_some_and(|ordering| ordering.is_le())
258            && self
259                .max
260                .cmp_value(&value)
261                .is_some_and(|ordering| ordering.is_ge())
262    }
263
264    pub(crate) fn contains_u64(&self, value: u64) -> bool {
265        let value = RangeBound::Unsigned(value);
266        self.min
267            .cmp_value(&value)
268            .is_some_and(|ordering| ordering.is_le())
269            && self
270                .max
271                .cmp_value(&value)
272                .is_some_and(|ordering| ordering.is_ge())
273    }
274}
275
276impl fmt::Display for Range {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        if self.min == self.max {
279            write!(f, "{}", self.min)
280        } else {
281            write!(f, "{}..{}", self.min, self.max)
282        }
283    }
284}
285
286/// A labeled integer from an enumeration or BITS definition.
287///
288/// Used in OBJECT-TYPE SYNTAX enumerations, BITS definitions, and
289/// refinement clauses in compliance and capability statements.
290#[derive(Debug, Clone)]
291pub struct NamedValue {
292    /// The textual label.
293    pub label: String,
294    /// The integer value associated with this label.
295    pub value: i64,
296    /// Source location of this named value.
297    pub range: SourceRange,
298}
299
300/// Finds a named value by label in a slice.
301pub(crate) fn find_named_value<'a>(
302    values: &'a [NamedValue],
303    label: &str,
304) -> Option<&'a NamedValue> {
305    values.iter().find(|nv| nv.label == label)
306}
307
308/// A module revision entry from a MODULE-IDENTITY REVISION clause.
309#[derive(Debug, Clone)]
310pub struct Revision {
311    /// Revision timestamp string.
312    pub date: String,
313    /// Free-text description of what changed.
314    pub description: String,
315    /// Source location of this revision clause.
316    pub range: SourceRange,
317}
318
319/// An index component from a table row's INDEX clause.
320///
321/// Indexes can be object-backed (referencing a column like `ifIndex`) or
322/// bare-type indexes (using a type name directly). The
323/// [`encoding`](Self::encoding) field indicates how this index component
324/// is encoded on the wire (see [`IndexEncoding`]).
325#[derive(Debug, Clone)]
326pub struct IndexEntry {
327    /// Name of the index object.
328    pub name: String,
329    /// Resolved object id, if found.
330    pub object: Option<ObjectId>,
331    /// Resolved type of the index object, if found.
332    pub type_id: Option<TypeId>,
333    /// True if this index uses the IMPLIED keyword.
334    pub implied: bool,
335    /// Wire encoding inferred from the index object's type.
336    pub encoding: IndexEncoding,
337    /// Source location of this index entry.
338    pub range: SourceRange,
339}
340
341/// Classify the index encoding from the object's resolved base type and size constraints.
342pub(crate) fn classify_index_encoding(
343    base: BaseType,
344    implied: bool,
345    sizes: &[Range],
346) -> IndexEncoding {
347    match base {
348        BaseType::Integer32
349        | BaseType::Unsigned32
350        | BaseType::Gauge32
351        | BaseType::TimeTicks
352        | BaseType::Counter32
353        | BaseType::Counter64 => IndexEncoding::Integer,
354        BaseType::IpAddress => IndexEncoding::IpAddress,
355        BaseType::OctetString | BaseType::Opaque | BaseType::Bits => {
356            if implied {
357                IndexEncoding::Implied
358            } else if is_fixed_size(sizes) {
359                IndexEncoding::FixedString
360            } else {
361                IndexEncoding::LengthPrefixed
362            }
363        }
364        BaseType::ObjectIdentifier => {
365            if implied {
366                IndexEncoding::Implied
367            } else {
368                IndexEncoding::LengthPrefixed
369            }
370        }
371        _ => IndexEncoding::Unknown,
372    }
373}
374
375pub(crate) fn is_fixed_size(sizes: &[Range]) -> bool {
376    sizes.len() == 1
377        && sizes[0].min == sizes[0].max
378        && sizes[0].min.as_u64().is_some_and(|value| value > 0)
379}
380
381/// Discriminant for the kind of value in a [`DefVal`].
382///
383/// Mirrors the [`DefValValue`] variants but as a simple `Copy` enum,
384/// useful for matching or display without borrowing the value.
385#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
386#[repr(u8)]
387pub enum DefValKind {
388    /// No default value specified.
389    Unset = 0,
390    /// Signed integer value.
391    Int = 1,
392    /// Unsigned integer value.
393    Uint = 2,
394    /// Quoted string value.
395    String = 3,
396    /// Raw byte sequence (hex string).
397    Bytes = 4,
398    /// Enumeration label.
399    Enum = 5,
400    /// Set of BITS labels.
401    Bits = 6,
402    /// Object identifier value.
403    Oid = 7,
404}
405
406impl fmt::Display for DefValKind {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        f.write_str(match self {
409            DefValKind::Unset => "unset",
410            DefValKind::Int => "int",
411            DefValKind::Uint => "uint",
412            DefValKind::String => "string",
413            DefValKind::Bytes => "bytes",
414            DefValKind::Enum => "enum",
415            DefValKind::Bits => "bits",
416            DefValKind::Oid => "oid",
417        })
418    }
419}
420
421/// A DEFVAL clause value with both the interpreted value and the raw MIB syntax string.
422///
423/// The [`kind`](DefVal::kind) method returns the discriminant, [`value`](DefVal::value)
424/// returns the interpreted value, and [`raw`](DefVal::raw) returns the original
425/// syntax as written in the MIB source.
426///
427/// Constructed via the named constructors ([`DefVal::int`], [`DefVal::string`], etc.).
428#[derive(Debug, Clone)]
429pub struct DefVal {
430    pub(crate) kind: DefValKind,
431    pub(crate) value: DefValValue,
432    pub(crate) raw: String,
433    pub(crate) oid_ref: Option<OidRef>,
434}
435
436/// The interpreted value of a DEFVAL clause.
437///
438/// Each variant corresponds to a [`DefValKind`] discriminant.
439#[derive(Debug, Clone)]
440pub enum DefValValue {
441    /// No value (corresponds to `DefValKind::Unset`).
442    None,
443    /// Signed integer.
444    Int(i64),
445    /// Unsigned integer.
446    Uint(u64),
447    /// Quoted string.
448    String(String),
449    /// Raw byte sequence.
450    Bytes(Vec<u8>),
451    /// Enumeration label.
452    Enum(String),
453    /// Set of BITS labels.
454    Bits(Vec<String>),
455    /// Object identifier.
456    Oid(Oid),
457}
458
459impl DefVal {
460    /// Create a default value indicating no value was specified.
461    pub fn unset() -> Self {
462        DefVal {
463            kind: DefValKind::Unset,
464            value: DefValValue::None,
465            raw: String::new(),
466            oid_ref: None,
467        }
468    }
469
470    /// Create a signed integer default value.
471    pub fn int(v: i64, raw: String) -> Self {
472        DefVal {
473            kind: DefValKind::Int,
474            value: DefValValue::Int(v),
475            raw,
476            oid_ref: None,
477        }
478    }
479
480    /// Create an unsigned integer default value.
481    pub fn uint(v: u64, raw: String) -> Self {
482        DefVal {
483            kind: DefValKind::Uint,
484            value: DefValValue::Uint(v),
485            raw,
486            oid_ref: None,
487        }
488    }
489
490    /// Create a quoted string default value.
491    pub fn string(v: String, raw: String) -> Self {
492        DefVal {
493            kind: DefValKind::String,
494            value: DefValValue::String(v),
495            raw,
496            oid_ref: None,
497        }
498    }
499
500    /// Create a raw byte sequence default value (from a hex string).
501    pub fn bytes(v: Vec<u8>, raw: String) -> Self {
502        DefVal {
503            kind: DefValKind::Bytes,
504            value: DefValValue::Bytes(v),
505            raw,
506            oid_ref: None,
507        }
508    }
509
510    /// Create an enumeration label default value.
511    pub fn enumeration(label: String, raw: String) -> Self {
512        DefVal {
513            kind: DefValKind::Enum,
514            value: DefValValue::Enum(label),
515            raw,
516            oid_ref: None,
517        }
518    }
519
520    /// Create a BITS set default value.
521    pub fn bits(labels: Vec<String>, raw: String) -> Self {
522        DefVal {
523            kind: DefValKind::Bits,
524            value: DefValValue::Bits(labels),
525            raw,
526            oid_ref: None,
527        }
528    }
529
530    /// Create an OID default value.
531    pub fn oid(oid: Oid, raw: String) -> Self {
532        DefVal {
533            kind: DefValKind::Oid,
534            value: DefValValue::Oid(oid),
535            raw,
536            oid_ref: None,
537        }
538    }
539
540    pub(crate) fn oid_with_ref(oid: Oid, raw: String, oid_ref: OidRef) -> Self {
541        DefVal {
542            kind: DefValKind::Oid,
543            value: DefValValue::Oid(oid),
544            raw,
545            oid_ref: Some(oid_ref),
546        }
547    }
548
549    /// Return the [`DefValKind`] discriminant.
550    pub fn kind(&self) -> DefValKind {
551        self.kind
552    }
553
554    /// Return the raw MIB syntax string as written in the source.
555    pub fn raw(&self) -> &str {
556        &self.raw
557    }
558
559    /// Return the interpreted [`DefValValue`].
560    pub fn value(&self) -> &DefValValue {
561        &self.value
562    }
563
564    /// Return the exact symbolic OID reference used by this default value.
565    pub fn oid_ref(&self) -> Option<&OidRef> {
566        self.oid_ref.as_ref()
567    }
568
569    /// Return `true` if no default value was specified.
570    pub fn is_unset(&self) -> bool {
571        self.kind == DefValKind::Unset
572    }
573}
574
575impl fmt::Display for DefVal {
576    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577        match &self.value {
578            DefValValue::None => Ok(()),
579            DefValValue::Int(v) => write!(f, "{v}"),
580            DefValValue::Uint(v) => write!(f, "{v}"),
581            DefValValue::String(v) => {
582                write!(f, "\"{}\"", v.replace('"', "\\\""))
583            }
584            DefValValue::Bytes(b) => {
585                write!(f, "0x")?;
586                for byte in b {
587                    write!(f, "{byte:02X}")?;
588                }
589                Ok(())
590            }
591            DefValValue::Enum(label) => f.write_str(label),
592            DefValValue::Bits(labels) => {
593                if labels.is_empty() {
594                    write!(f, "{{ }}")
595                } else {
596                    write!(f, "{{ {} }}", labels.join(", "))
597                }
598            }
599            DefValValue::Oid(_) => f.write_str(&self.raw),
600        }
601    }
602}
603
604/// A MODULE clause within a [`ComplianceData`](super::compliance::ComplianceData) definition.
605///
606/// Specifies the mandatory groups and optional object refinements required
607/// for conformance to a particular module.
608#[derive(Debug, Clone)]
609pub struct ComplianceModule {
610    /// Name of the module this clause applies to.
611    pub module_name: String,
612    /// Groups required for conformance.
613    pub mandatory_groups: Vec<String>,
614    /// Optional GROUP refinements.
615    pub groups: Vec<ComplianceGroup>,
616    /// Optional OBJECT refinements.
617    pub objects: Vec<ComplianceObject>,
618    /// Source location of this MODULE clause.
619    pub range: SourceRange,
620}
621
622/// A GROUP clause within a [`ComplianceModule`].
623///
624/// Represents a conditionally required group, with a description of the
625/// conditions under which it is required.
626#[derive(Debug, Clone)]
627pub struct ComplianceGroup {
628    /// Name of the conditionally required group.
629    pub group: String,
630    /// Description of when this group is required.
631    pub description: String,
632    /// Source location of this GROUP clause.
633    pub range: SourceRange,
634}
635
636/// An OBJECT refinement within a [`ComplianceModule`].
637///
638/// May narrow the syntax, write-syntax, or minimum access level for an
639/// object beyond what the base OBJECT-TYPE definition requires.
640#[derive(Debug, Clone)]
641pub struct ComplianceObject {
642    /// Name of the refined object.
643    pub object: String,
644    /// Restricted SYNTAX, if any.
645    pub syntax: Option<SyntaxConstraints>,
646    /// Restricted WRITE-SYNTAX, if any.
647    pub write_syntax: Option<SyntaxConstraints>,
648    /// Minimum required access level, if specified.
649    pub min_access: Option<Access>,
650    /// Description of the refinement.
651    pub description: String,
652    /// Source location of this OBJECT clause.
653    pub range: SourceRange,
654}
655
656/// A SUPPORTS clause within a [`CapabilityData`](super::capability::CapabilityData) definition.
657///
658/// Lists the included groups from a supported module and any object or
659/// notification variations the agent implements.
660#[derive(Debug, Clone)]
661pub struct CapabilitiesModule {
662    /// Name of the supported module.
663    pub module_name: String,
664    /// Groups included from this module.
665    pub includes: Vec<String>,
666    /// Object VARIATION clauses.
667    pub object_variations: Vec<ObjectVariation>,
668    /// Notification VARIATION clauses.
669    pub notification_variations: Vec<NotificationVariation>,
670    /// Source location of this SUPPORTS clause.
671    pub range: SourceRange,
672}
673
674/// An object VARIATION within a [`CapabilitiesModule`].
675///
676/// Describes implementation-specific deviations for a single object,
677/// including restricted syntax, access overrides, and default values.
678#[derive(Debug, Clone)]
679pub struct ObjectVariation {
680    /// Name of the varied object.
681    pub object: String,
682    /// Restricted SYNTAX, if any.
683    pub syntax: Option<SyntaxConstraints>,
684    /// Restricted WRITE-SYNTAX, if any.
685    pub write_syntax: Option<SyntaxConstraints>,
686    /// Overridden access level, if any.
687    pub access: Option<Access>,
688    /// Objects required for row creation, with their exact resolved defining
689    /// module and OID when available.
690    pub creation_requires: Vec<OidRef>,
691    /// Implementation-specific default value, if any.
692    pub def_val: Option<DefVal>,
693    /// Description of this variation.
694    pub description: String,
695    /// Source location of this VARIATION clause.
696    pub range: SourceRange,
697}
698
699/// A notification VARIATION within a [`CapabilitiesModule`].
700///
701/// Describes implementation-specific deviations for a single notification.
702#[derive(Debug, Clone)]
703pub struct NotificationVariation {
704    /// Name of the varied notification.
705    pub notification: String,
706    /// Overridden access level, if any.
707    pub access: Option<Access>,
708    /// Description of this variation.
709    pub description: String,
710    /// Source location of this VARIATION clause.
711    pub range: SourceRange,
712}
713
714/// Inline syntax constraints from a VARIATION SYNTAX/WRITE-SYNTAX clause
715/// or a MODULE-COMPLIANCE OBJECT refinement.
716///
717/// Represents a restricted view of a type with narrowed ranges, enums, or
718/// BITS values.
719#[derive(Debug, Clone)]
720pub struct SyntaxConstraints {
721    /// Resolved type, if any.
722    pub type_id: Option<TypeId>,
723    /// Effective SIZE constraints after intersection with the resolved type.
724    pub sizes: Vec<Range>,
725    /// SIZE constraints declared directly in this syntax clause.
726    pub declared_sizes: Vec<Range>,
727    /// Whether a SIZE constraint was explicitly declared.
728    ///
729    /// When this is true and `sizes` is empty, the declared constraint has an
730    /// empty intersection with the inherited or base-type constraint.
731    pub sizes_constrained: bool,
732    /// Effective value range constraints after intersection with the resolved type.
733    pub ranges: Vec<Range>,
734    /// Value range constraints declared directly in this syntax clause.
735    pub declared_ranges: Vec<Range>,
736    /// Whether a value range constraint was explicitly declared.
737    ///
738    /// When this is true and `ranges` is empty, the declared constraint has an
739    /// empty intersection with the inherited or base-type constraint.
740    pub ranges_constrained: bool,
741    /// Restricted enumeration values.
742    pub enums: Vec<NamedValue>,
743    /// Restricted BITS values.
744    pub bits: Vec<NamedValue>,
745}
746
747/// SMIv1 TRAP-TYPE specific fields.
748///
749/// Present on [`NotificationData`](super::notification::NotificationData)
750/// instances that originate from TRAP-TYPE definitions.
751#[derive(Debug, Clone)]
752pub struct TrapInfo {
753    /// ENTERPRISE OID name from the TRAP-TYPE definition.
754    pub enterprise: String,
755    /// Numeric trap identifier (the specific-trap number).
756    pub trap_number: u32,
757}
758
759/// Identifies the category of an unresolved reference.
760#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
761#[repr(u8)]
762pub enum UnresolvedKind {
763    /// An unresolved IMPORTS symbol.
764    Import = 0,
765    /// An unresolved type reference.
766    Type = 1,
767    /// An unresolved OID component.
768    Oid = 2,
769    /// An unresolved INDEX object.
770    Index = 3,
771    /// An unresolved OBJECTS member of a notification.
772    NotificationObject = 4,
773}
774
775impl fmt::Display for UnresolvedKind {
776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777        f.write_str(match self {
778            UnresolvedKind::Import => "import",
779            UnresolvedKind::Type => "type",
780            UnresolvedKind::Oid => "oid",
781            UnresolvedKind::Index => "index",
782            UnresolvedKind::NotificationObject => "notification-object",
783        })
784    }
785}
786
787/// An unresolved symbol reference collected during resolution.
788///
789/// Available via [`Mib::unresolved`](super::mib::Mib::unresolved).
790#[derive(Debug, Clone)]
791pub struct UnresolvedRef {
792    /// What kind of reference failed to resolve.
793    pub kind: UnresolvedKind,
794    /// The symbol name that could not be resolved.
795    pub symbol: String,
796    /// The module where the reference was used.
797    pub module: String,
798    /// Human-readable explanation of why resolution failed.
799    pub reason: String,
800}
801
802/// A symbolic reference to an OID-bearing definition.
803///
804/// Used both for OID value-assignment components (for example `enterprises` in
805/// `{ enterprises 9 }`) and for resolved conformance references that need to
806/// retain exact defining-module provenance.
807#[derive(Debug, Clone)]
808pub struct OidRef {
809    /// The symbolic name referenced in the OID assignment.
810    pub name: String,
811    /// Source location of this reference.
812    pub range: SourceRange,
813    pub(crate) module: Option<ModuleId>,
814    pub(crate) oid: Option<Oid>,
815}
816
817impl OidRef {
818    /// Return the exact resolved defining module/version, when known.
819    pub fn module_id(&self) -> Option<ModuleId> {
820        self.module
821    }
822
823    /// Return the resolved numeric OID of the referenced symbol, when known.
824    pub fn oid(&self) -> Option<&Oid> {
825        self.oid.as_ref()
826    }
827}
828
829// Arena index types for the resolved model.
830macro_rules! define_id {
831    ($(#[$attr:meta])* $name:ident) => {
832        $(#[$attr])*
833        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
834        pub struct $name(pub(crate) u32);
835
836        impl $name {
837            pub(crate) fn new(index: u32) -> Self {
838                Self(index)
839            }
840
841            /// Return the raw arena index as a `u32`.
842            pub fn index(self) -> u32 {
843                self.0
844            }
845        }
846    };
847}
848
849define_id!(
850    /// Index into the OidTree's node arena.
851    NodeId
852);
853define_id!(
854    /// Index into the Mib's object arena.
855    ObjectId
856);
857define_id!(
858    /// Index into the Mib's type arena.
859    TypeId
860);
861define_id!(
862    /// Index into the Mib's module arena.
863    ModuleId
864);
865define_id!(
866    /// Index into the Mib's notification arena.
867    NotificationId
868);
869define_id!(
870    /// Index into the Mib's group arena.
871    GroupId
872);
873define_id!(
874    /// Index into the Mib's compliance arena.
875    ComplianceId
876);
877define_id!(
878    /// Index into the Mib's capability arena.
879    CapabilityId
880);