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::types::{Access, BaseType, IndexEncoding, Span};
12
13/// A single imported symbol with its source location.
14///
15/// Part of an [`Import`] group. The `name` is the symbol as written in the
16/// MIB's IMPORTS clause (e.g. `"ifIndex"`, `"DisplayString"`).
17#[derive(Debug, Clone)]
18pub struct ImportSymbol {
19    /// The symbol name as it appears in the IMPORTS clause.
20    pub name: String,
21    /// Source location of this symbol reference.
22    pub span: Span,
23}
24
25/// A group of symbols imported from a single source module.
26///
27/// Each MIB module's IMPORTS section is represented as a list of `Import`
28/// entries, one per source module.
29#[derive(Debug, Clone)]
30pub struct Import {
31    /// Name of the module being imported from.
32    pub module: String,
33    /// Symbols imported from this module.
34    pub symbols: Vec<ImportSymbol>,
35}
36
37/// A min..max constraint range, used for both SIZE and value constraints.
38///
39/// For single-value constraints (e.g. `SIZE (6)`), `min` equals `max`.
40#[derive(Debug, Clone, Copy)]
41pub struct Range {
42    /// Lower bound (inclusive).
43    pub min: i64,
44    /// Upper bound (inclusive). Equal to `min` for single-value ranges.
45    pub max: i64,
46    /// Source location of this constraint.
47    pub span: Span,
48}
49
50impl fmt::Display for Range {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        if self.min == self.max {
53            write!(f, "{}", self.min)
54        } else {
55            write!(f, "{}..{}", self.min, self.max)
56        }
57    }
58}
59
60/// A labeled integer from an enumeration or BITS definition.
61///
62/// Used in OBJECT-TYPE SYNTAX enumerations, BITS definitions, and
63/// refinement clauses in compliance and capability statements.
64#[derive(Debug, Clone)]
65pub struct NamedValue {
66    /// The textual label.
67    pub label: String,
68    /// The integer value associated with this label.
69    pub value: i64,
70    /// Source location of this named value.
71    pub span: Span,
72}
73
74/// Finds a named value by label in a slice.
75pub(crate) fn find_named_value<'a>(
76    values: &'a [NamedValue],
77    label: &str,
78) -> Option<&'a NamedValue> {
79    values.iter().find(|nv| nv.label == label)
80}
81
82/// A module revision entry from a MODULE-IDENTITY REVISION clause.
83#[derive(Debug, Clone)]
84pub struct Revision {
85    /// Revision timestamp string.
86    pub date: String,
87    /// Free-text description of what changed.
88    pub description: String,
89    /// Source location of this revision clause.
90    pub span: Span,
91}
92
93/// An index component from a table row's INDEX clause.
94///
95/// Indexes can be object-backed (referencing a column like `ifIndex`) or
96/// bare-type indexes (using a type name directly). The
97/// [`encoding`](Self::encoding) field indicates how this index component
98/// is encoded on the wire (see [`IndexEncoding`]).
99#[derive(Debug, Clone)]
100pub struct IndexEntry {
101    /// Name of the index object.
102    pub name: String,
103    /// Resolved object id, if found.
104    pub object: Option<ObjectId>,
105    /// Resolved type of the index object, if found.
106    pub type_id: Option<TypeId>,
107    /// True if this index uses the IMPLIED keyword.
108    pub implied: bool,
109    /// Wire encoding inferred from the index object's type.
110    pub encoding: IndexEncoding,
111    /// Source location of this index entry.
112    pub span: Span,
113}
114
115/// Classify the index encoding from the object's resolved base type and size constraints.
116pub(crate) fn classify_index_encoding(
117    base: BaseType,
118    implied: bool,
119    sizes: &[Range],
120) -> IndexEncoding {
121    match base {
122        BaseType::Integer32
123        | BaseType::Unsigned32
124        | BaseType::Gauge32
125        | BaseType::TimeTicks
126        | BaseType::Counter32
127        | BaseType::Counter64 => IndexEncoding::Integer,
128        BaseType::IpAddress => IndexEncoding::IpAddress,
129        BaseType::OctetString | BaseType::Opaque | BaseType::Bits => {
130            if implied {
131                IndexEncoding::Implied
132            } else if is_fixed_size(sizes) {
133                IndexEncoding::FixedString
134            } else {
135                IndexEncoding::LengthPrefixed
136            }
137        }
138        BaseType::ObjectIdentifier => {
139            if implied {
140                IndexEncoding::Implied
141            } else {
142                IndexEncoding::LengthPrefixed
143            }
144        }
145        _ => IndexEncoding::Unknown,
146    }
147}
148
149pub(crate) fn is_fixed_size(sizes: &[Range]) -> bool {
150    sizes.len() == 1 && sizes[0].min == sizes[0].max && sizes[0].min > 0
151}
152
153/// Discriminant for the kind of value in a [`DefVal`].
154///
155/// Mirrors the [`DefValValue`] variants but as a simple `Copy` enum,
156/// useful for matching or display without borrowing the value.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158#[repr(u8)]
159pub enum DefValKind {
160    /// No default value specified.
161    Unset = 0,
162    /// Signed integer value.
163    Int = 1,
164    /// Unsigned integer value.
165    Uint = 2,
166    /// Quoted string value.
167    String = 3,
168    /// Raw byte sequence (hex string).
169    Bytes = 4,
170    /// Enumeration label.
171    Enum = 5,
172    /// Set of BITS labels.
173    Bits = 6,
174    /// Object identifier value.
175    Oid = 7,
176}
177
178impl fmt::Display for DefValKind {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.write_str(match self {
181            DefValKind::Unset => "unset",
182            DefValKind::Int => "int",
183            DefValKind::Uint => "uint",
184            DefValKind::String => "string",
185            DefValKind::Bytes => "bytes",
186            DefValKind::Enum => "enum",
187            DefValKind::Bits => "bits",
188            DefValKind::Oid => "oid",
189        })
190    }
191}
192
193/// A DEFVAL clause value with both the interpreted value and the raw MIB syntax string.
194///
195/// The [`kind`](DefVal::kind) method returns the discriminant, [`value`](DefVal::value)
196/// returns the interpreted value, and [`raw`](DefVal::raw) returns the original
197/// syntax as written in the MIB source.
198///
199/// Constructed via the named constructors ([`DefVal::int`], [`DefVal::string`], etc.).
200#[derive(Debug, Clone)]
201pub struct DefVal {
202    pub(crate) kind: DefValKind,
203    pub(crate) value: DefValValue,
204    pub(crate) raw: String,
205}
206
207/// The interpreted value of a DEFVAL clause.
208///
209/// Each variant corresponds to a [`DefValKind`] discriminant.
210#[derive(Debug, Clone)]
211pub enum DefValValue {
212    /// No value (corresponds to `DefValKind::Unset`).
213    None,
214    /// Signed integer.
215    Int(i64),
216    /// Unsigned integer.
217    Uint(u64),
218    /// Quoted string.
219    String(String),
220    /// Raw byte sequence.
221    Bytes(Vec<u8>),
222    /// Enumeration label.
223    Enum(String),
224    /// Set of BITS labels.
225    Bits(Vec<String>),
226    /// Object identifier.
227    Oid(Oid),
228}
229
230impl DefVal {
231    /// Create a default value indicating no value was specified.
232    pub fn unset() -> Self {
233        DefVal {
234            kind: DefValKind::Unset,
235            value: DefValValue::None,
236            raw: String::new(),
237        }
238    }
239
240    /// Create a signed integer default value.
241    pub fn int(v: i64, raw: String) -> Self {
242        DefVal {
243            kind: DefValKind::Int,
244            value: DefValValue::Int(v),
245            raw,
246        }
247    }
248
249    /// Create an unsigned integer default value.
250    pub fn uint(v: u64, raw: String) -> Self {
251        DefVal {
252            kind: DefValKind::Uint,
253            value: DefValValue::Uint(v),
254            raw,
255        }
256    }
257
258    /// Create a quoted string default value.
259    pub fn string(v: String, raw: String) -> Self {
260        DefVal {
261            kind: DefValKind::String,
262            value: DefValValue::String(v),
263            raw,
264        }
265    }
266
267    /// Create a raw byte sequence default value (from a hex string).
268    pub fn bytes(v: Vec<u8>, raw: String) -> Self {
269        DefVal {
270            kind: DefValKind::Bytes,
271            value: DefValValue::Bytes(v),
272            raw,
273        }
274    }
275
276    /// Create an enumeration label default value.
277    pub fn enumeration(label: String, raw: String) -> Self {
278        DefVal {
279            kind: DefValKind::Enum,
280            value: DefValValue::Enum(label),
281            raw,
282        }
283    }
284
285    /// Create a BITS set default value.
286    pub fn bits(labels: Vec<String>, raw: String) -> Self {
287        DefVal {
288            kind: DefValKind::Bits,
289            value: DefValValue::Bits(labels),
290            raw,
291        }
292    }
293
294    /// Create an OID default value.
295    pub fn oid(oid: Oid, raw: String) -> Self {
296        DefVal {
297            kind: DefValKind::Oid,
298            value: DefValValue::Oid(oid),
299            raw,
300        }
301    }
302
303    /// Return the [`DefValKind`] discriminant.
304    pub fn kind(&self) -> DefValKind {
305        self.kind
306    }
307
308    /// Return the raw MIB syntax string as written in the source.
309    pub fn raw(&self) -> &str {
310        &self.raw
311    }
312
313    /// Return the interpreted [`DefValValue`].
314    pub fn value(&self) -> &DefValValue {
315        &self.value
316    }
317
318    /// Return `true` if no default value was specified.
319    pub fn is_unset(&self) -> bool {
320        self.kind == DefValKind::Unset
321    }
322}
323
324impl fmt::Display for DefVal {
325    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326        match &self.value {
327            DefValValue::None => Ok(()),
328            DefValValue::Int(v) => write!(f, "{v}"),
329            DefValValue::Uint(v) => write!(f, "{v}"),
330            DefValValue::String(v) => {
331                write!(f, "\"{}\"", v.replace('"', "\\\""))
332            }
333            DefValValue::Bytes(b) => {
334                if b.is_empty() {
335                    write!(f, "0")
336                } else if b.len() <= 8 {
337                    let mut n: u64 = 0;
338                    for &byte in b {
339                        n = n << 8 | byte as u64;
340                    }
341                    write!(f, "{n}")
342                } else {
343                    write!(f, "0x")?;
344                    for byte in b {
345                        write!(f, "{byte:02X}")?;
346                    }
347                    Ok(())
348                }
349            }
350            DefValValue::Enum(label) => f.write_str(label),
351            DefValValue::Bits(labels) => {
352                if labels.is_empty() {
353                    write!(f, "{{ }}")
354                } else {
355                    write!(f, "{{ {} }}", labels.join(", "))
356                }
357            }
358            DefValValue::Oid(_) => f.write_str(&self.raw),
359        }
360    }
361}
362
363/// A MODULE clause within a [`ComplianceData`](super::compliance::ComplianceData) definition.
364///
365/// Specifies the mandatory groups and optional object refinements required
366/// for conformance to a particular module.
367#[derive(Debug, Clone)]
368pub struct ComplianceModule {
369    /// Name of the module this clause applies to.
370    pub module_name: String,
371    /// Groups required for conformance.
372    pub mandatory_groups: Vec<String>,
373    /// Optional GROUP refinements.
374    pub groups: Vec<ComplianceGroup>,
375    /// Optional OBJECT refinements.
376    pub objects: Vec<ComplianceObject>,
377    /// Source location of this MODULE clause.
378    pub span: Span,
379}
380
381/// A GROUP clause within a [`ComplianceModule`].
382///
383/// Represents a conditionally required group, with a description of the
384/// conditions under which it is required.
385#[derive(Debug, Clone)]
386pub struct ComplianceGroup {
387    /// Name of the conditionally required group.
388    pub group: String,
389    /// Description of when this group is required.
390    pub description: String,
391    /// Source location of this GROUP clause.
392    pub span: Span,
393}
394
395/// An OBJECT refinement within a [`ComplianceModule`].
396///
397/// May narrow the syntax, write-syntax, or minimum access level for an
398/// object beyond what the base OBJECT-TYPE definition requires.
399#[derive(Debug, Clone)]
400pub struct ComplianceObject {
401    /// Name of the refined object.
402    pub object: String,
403    /// Restricted SYNTAX, if any.
404    pub syntax: Option<SyntaxConstraints>,
405    /// Restricted WRITE-SYNTAX, if any.
406    pub write_syntax: Option<SyntaxConstraints>,
407    /// Minimum required access level, if specified.
408    pub min_access: Option<Access>,
409    /// Description of the refinement.
410    pub description: String,
411    /// Source location of this OBJECT clause.
412    pub span: Span,
413}
414
415/// A SUPPORTS clause within a [`CapabilityData`](super::capability::CapabilityData) definition.
416///
417/// Lists the included groups from a supported module and any object or
418/// notification variations the agent implements.
419#[derive(Debug, Clone)]
420pub struct CapabilitiesModule {
421    /// Name of the supported module.
422    pub module_name: String,
423    /// Groups included from this module.
424    pub includes: Vec<String>,
425    /// Object VARIATION clauses.
426    pub object_variations: Vec<ObjectVariation>,
427    /// Notification VARIATION clauses.
428    pub notification_variations: Vec<NotificationVariation>,
429    /// Source location of this SUPPORTS clause.
430    pub span: Span,
431}
432
433/// An object VARIATION within a [`CapabilitiesModule`].
434///
435/// Describes implementation-specific deviations for a single object,
436/// including restricted syntax, access overrides, and default values.
437#[derive(Debug, Clone)]
438pub struct ObjectVariation {
439    /// Name of the varied object.
440    pub object: String,
441    /// Restricted SYNTAX, if any.
442    pub syntax: Option<SyntaxConstraints>,
443    /// Restricted WRITE-SYNTAX, if any.
444    pub write_syntax: Option<SyntaxConstraints>,
445    /// Overridden access level, if any.
446    pub access: Option<Access>,
447    /// Objects required for row creation.
448    pub creation_requires: Vec<String>,
449    /// Implementation-specific default value, if any.
450    pub def_val: Option<DefVal>,
451    /// Description of this variation.
452    pub description: String,
453    /// Source location of this VARIATION clause.
454    pub span: Span,
455}
456
457/// A notification VARIATION within a [`CapabilitiesModule`].
458///
459/// Describes implementation-specific deviations for a single notification.
460#[derive(Debug, Clone)]
461pub struct NotificationVariation {
462    /// Name of the varied notification.
463    pub notification: String,
464    /// Overridden access level, if any.
465    pub access: Option<Access>,
466    /// Description of this variation.
467    pub description: String,
468    /// Source location of this VARIATION clause.
469    pub span: Span,
470}
471
472/// Inline syntax constraints from a VARIATION SYNTAX/WRITE-SYNTAX clause
473/// or a MODULE-COMPLIANCE OBJECT refinement.
474///
475/// Represents a restricted view of a type with narrowed ranges, enums, or
476/// BITS values.
477#[derive(Debug, Clone)]
478pub struct SyntaxConstraints {
479    /// Resolved type, if any.
480    pub type_id: Option<TypeId>,
481    /// SIZE constraints.
482    pub sizes: Vec<Range>,
483    /// Value range constraints.
484    pub ranges: Vec<Range>,
485    /// Restricted enumeration values.
486    pub enums: Vec<NamedValue>,
487    /// Restricted BITS values.
488    pub bits: Vec<NamedValue>,
489}
490
491/// SMIv1 TRAP-TYPE specific fields.
492///
493/// Present on [`NotificationData`](super::notification::NotificationData)
494/// instances that originate from TRAP-TYPE definitions.
495#[derive(Debug, Clone)]
496pub struct TrapInfo {
497    /// ENTERPRISE OID name from the TRAP-TYPE definition.
498    pub enterprise: String,
499    /// Numeric trap identifier (the specific-trap number).
500    pub trap_number: u32,
501}
502
503/// Identifies the category of an unresolved reference.
504#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
505#[repr(u8)]
506pub enum UnresolvedKind {
507    /// An unresolved IMPORTS symbol.
508    Import = 0,
509    /// An unresolved type reference.
510    Type = 1,
511    /// An unresolved OID component.
512    Oid = 2,
513    /// An unresolved INDEX object.
514    Index = 3,
515    /// An unresolved OBJECTS member of a notification.
516    NotificationObject = 4,
517}
518
519impl fmt::Display for UnresolvedKind {
520    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521        f.write_str(match self {
522            UnresolvedKind::Import => "import",
523            UnresolvedKind::Type => "type",
524            UnresolvedKind::Oid => "oid",
525            UnresolvedKind::Index => "index",
526            UnresolvedKind::NotificationObject => "notification-object",
527        })
528    }
529}
530
531/// An unresolved symbol reference collected during resolution.
532///
533/// Available via [`Mib::unresolved`](super::mib::Mib::unresolved).
534#[derive(Debug, Clone)]
535pub struct UnresolvedRef {
536    /// What kind of reference failed to resolve.
537    pub kind: UnresolvedKind,
538    /// The symbol name that could not be resolved.
539    pub symbol: String,
540    /// The module where the reference was used.
541    pub module: String,
542    /// Human-readable explanation of why resolution failed.
543    pub reason: String,
544}
545
546/// A symbolic name referenced in an OID value assignment (e.g. `enterprises` in
547/// `{ enterprises 9 }`).
548#[derive(Debug, Clone)]
549pub struct OidRef {
550    /// The symbolic name referenced in the OID assignment.
551    pub name: String,
552    /// Source location of this reference.
553    pub span: Span,
554}
555
556// Arena index types for the resolved model.
557macro_rules! define_id {
558    ($(#[$attr:meta])* $name:ident) => {
559        $(#[$attr])*
560        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
561        pub struct $name(pub(crate) u32);
562
563        impl $name {
564            pub(crate) fn new(index: u32) -> Self {
565                Self(index)
566            }
567
568            /// Return the raw arena index as a `u32`.
569            pub fn index(self) -> u32 {
570                self.0
571            }
572        }
573    };
574}
575
576define_id!(
577    /// Index into the OidTree's node arena.
578    NodeId
579);
580define_id!(
581    /// Index into the Mib's object arena.
582    ObjectId
583);
584define_id!(
585    /// Index into the Mib's type arena.
586    TypeId
587);
588define_id!(
589    /// Index into the Mib's module arena.
590    ModuleId
591);
592define_id!(
593    /// Index into the Mib's notification arena.
594    NotificationId
595);
596define_id!(
597    /// Index into the Mib's group arena.
598    GroupId
599);
600define_id!(
601    /// Index into the Mib's compliance arena.
602    ComplianceId
603);
604define_id!(
605    /// Index into the Mib's capability arena.
606    CapabilityId
607);