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);