mib_rs/types/enums.rs
1//! Enumerations for SMI concepts.
2//!
3//! Defines the core enums used throughout the MIB parsing and resolution pipeline:
4//! severity levels, node kinds, access levels, status values, base types, and
5//! configuration knobs for resolver strictness and diagnostic reporting.
6
7use std::fmt;
8
9macro_rules! impl_display {
10 ($ty:ident { $($variant:ident => $s:literal),* $(,)? }) => {
11 impl fmt::Display for $ty {
12 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13 f.write_str(match self {
14 $($ty::$variant => $s),*
15 })
16 }
17 }
18 }
19}
20
21/// Severity indicates how serious a diagnostic issue is (libsmi-compatible).
22/// Lower values are more severe.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[repr(u8)]
25pub enum Severity {
26 /// Unrecoverable failure that halts processing.
27 Fatal = 0,
28 /// Serious issue that likely produces incorrect results.
29 Severe = 1,
30 /// Standard error in the MIB definition.
31 Error = 2,
32 /// Minor issue that may indicate a problem.
33 Minor = 3,
34 /// Stylistic deviation from best practice.
35 Style = 4,
36 /// Potential issue worth noting.
37 Warning = 5,
38 /// Informational message.
39 Info = 6,
40}
41
42impl Severity {
43 /// Reports whether this severity is at least as severe as `threshold`.
44 pub fn at_least(self, threshold: Severity) -> bool {
45 self <= threshold
46 }
47}
48
49impl_display!(Severity {
50 Fatal => "fatal",
51 Severe => "severe",
52 Error => "error",
53 Minor => "minor",
54 Style => "style",
55 Warning => "warning",
56 Info => "info",
57});
58
59/// Controls resolver fallback behavior when resolving cross-module references.
60///
61/// Ordered from strictest (fewest fallbacks) to most permissive.
62/// See also [`ReportingLevel`] which controls diagnostic output separately.
63///
64/// All levels support direct import resolution, import forwarding (following
65/// re-exports declared in the source module's own IMPORTS), partial import
66/// resolution, ASN.1 primitive type fallback, and well-known OID roots.
67///
68/// See the crate-level docs for a detailed breakdown of behaviors per level.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70#[repr(u8)]
71pub enum ResolverStrictness {
72 /// Minimal fallbacks. Only deterministic strategies that don't guess
73 /// the source module (direct imports, import forwarding, ASN.1
74 /// primitives, well-known OID roots).
75 Strict = 0,
76 /// Constrained fallbacks: module name aliases, unimported SMI/TC type
77 /// lookup, SMI global OID root fallback, TRAP-TYPE enterprise lookup.
78 Normal = 1,
79 /// All fallbacks, including global symbol search across all loaded
80 /// modules for objects, group members, and compliance targets.
81 Permissive = 2,
82}
83
84impl ResolverStrictness {
85 /// Reports whether tier-2 constrained fallbacks are enabled (Normal+).
86 pub fn allow_constrained_fallbacks(self) -> bool {
87 self != ResolverStrictness::Strict
88 }
89
90 /// Reports whether tier-3 global fallbacks are enabled (Permissive only).
91 pub fn allow_global_fallbacks(self) -> bool {
92 self == ResolverStrictness::Permissive
93 }
94}
95
96impl_display!(ResolverStrictness {
97 Strict => "strict",
98 Normal => "normal",
99 Permissive => "permissive",
100});
101
102/// Controls diagnostic reporting verbosity.
103///
104/// See also [`ResolverStrictness`] which controls resolver fallback behavior separately.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106#[repr(u8)]
107pub enum ReportingLevel {
108 /// Suppress all diagnostics except fatal errors.
109 Silent = 0,
110 /// Report errors and above only.
111 Quiet = 1,
112 /// Report minor issues and above.
113 Default = 2,
114 /// Report all diagnostics including style and info.
115 Verbose = 3,
116}
117
118impl_display!(ReportingLevel {
119 Silent => "silent",
120 Quiet => "quiet",
121 Default => "default",
122 Verbose => "verbose",
123});
124
125/// Identifies what an OID node represents in the MIB tree.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
127#[repr(u8)]
128pub enum Kind {
129 /// Kind not yet determined.
130 #[default]
131 Unknown = 0,
132 /// Synthetic internal node (e.g. root of the OID tree).
133 Internal = 1,
134 /// Plain OID registration (OBJECT IDENTIFIER value assignment).
135 Node = 2,
136 /// Scalar OBJECT-TYPE (single-instance managed object).
137 Scalar = 3,
138 /// Table OBJECT-TYPE (SEQUENCE OF).
139 Table = 4,
140 /// Row OBJECT-TYPE (conceptual row / SEQUENCE entry).
141 Row = 5,
142 /// Column OBJECT-TYPE (leaf within a row).
143 Column = 6,
144 /// NOTIFICATION-TYPE or TRAP-TYPE definition.
145 Notification = 7,
146 /// OBJECT-GROUP or NOTIFICATION-GROUP.
147 Group = 8,
148 /// MODULE-COMPLIANCE definition.
149 Compliance = 9,
150 /// AGENT-CAPABILITIES definition.
151 Capability = 10,
152 /// MODULE-IDENTITY definition.
153 ModuleIdentity = 11,
154 /// OBJECT-IDENTITY definition.
155 ObjectIdentity = 12,
156}
157
158impl Kind {
159 /// Reports whether this is a scalar/table/row/column.
160 pub fn is_object_type(self) -> bool {
161 matches!(self, Kind::Scalar | Kind::Table | Kind::Row | Kind::Column)
162 }
163
164 /// Reports whether this is a group/compliance/capabilities node.
165 pub fn is_conformance(self) -> bool {
166 matches!(self, Kind::Group | Kind::Compliance | Kind::Capability)
167 }
168
169 /// Reports whether this is a plain node-like kind (node, module-identity, object-identity).
170 pub fn is_node_like(self) -> bool {
171 matches!(
172 self,
173 Kind::Node | Kind::ModuleIdentity | Kind::ObjectIdentity
174 )
175 }
176}
177
178impl_display!(Kind {
179 Unknown => "unknown",
180 Internal => "internal",
181 Node => "node",
182 Scalar => "scalar",
183 Table => "table",
184 Row => "row",
185 Column => "column",
186 Notification => "notification",
187 Group => "group",
188 Compliance => "compliance",
189 Capability => "capabilities",
190 ModuleIdentity => "module-identity",
191 ObjectIdentity => "object-identity",
192});
193
194/// Access level for OBJECT-TYPE definitions.
195///
196/// Covers both SMIv1 ACCESS and SMIv2 MAX-ACCESS values.
197/// See [`AccessKeyword`] for which keyword was used in the source.
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
199#[repr(u8)]
200pub enum Access {
201 /// Object cannot be read or written.
202 #[default]
203 NotAccessible = 0,
204 /// Object is only accessible via notifications.
205 AccessibleForNotify = 1,
206 /// Object can be read but not written.
207 ReadOnly = 2,
208 /// Object can be read and written.
209 ReadWrite = 3,
210 /// Object can be read, written, and used in row creation.
211 ReadCreate = 4,
212 /// Object can only be written (SMIv1 only, deprecated in SMIv2).
213 WriteOnly = 5,
214 /// Object is not implemented (AGENT-CAPABILITIES variation).
215 NotImplemented = 6,
216}
217
218impl_display!(Access {
219 NotAccessible => "not-accessible",
220 AccessibleForNotify => "accessible-for-notify",
221 ReadOnly => "read-only",
222 ReadWrite => "read-write",
223 ReadCreate => "read-create",
224 WriteOnly => "write-only",
225 NotImplemented => "not-implemented",
226});
227
228/// Lifecycle state of a MIB definition.
229///
230/// SMIv2 uses `Current`, `Deprecated`, and `Obsolete`. SMIv1 additionally uses
231/// `Mandatory` and `Optional`. Values are not normalized across versions.
232#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
233#[repr(u8)]
234pub enum Status {
235 /// Active and valid (SMIv2).
236 #[default]
237 Current = 0,
238 /// Still usable but being phased out.
239 Deprecated = 1,
240 /// No longer in use.
241 Obsolete = 2,
242 /// Required for compliance (SMIv1 only).
243 Mandatory = 3,
244 /// Not required (SMIv1 only).
245 Optional = 4,
246}
247
248impl Status {
249 /// Reports whether this is an SMIv1-specific status value.
250 pub fn is_smiv1(self) -> bool {
251 matches!(self, Status::Mandatory | Status::Optional)
252 }
253}
254
255impl_display!(Status {
256 Current => "current",
257 Deprecated => "deprecated",
258 Obsolete => "obsolete",
259 Mandatory => "mandatory",
260 Optional => "optional",
261});
262
263/// SMI language version of a MIB module.
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
265#[repr(u8)]
266pub enum Language {
267 /// Version not yet determined.
268 #[default]
269 Unknown = 0,
270 /// RFC 1155/1212 (Structure of Management Information v1).
271 SMIv1 = 1,
272 /// RFC 2578 (Structure of Management Information v2).
273 SMIv2 = 2,
274 /// RFC 3159 (Structure of Policy Provisioning Information).
275 SPPI = 3,
276}
277
278impl_display!(Language {
279 Unknown => "unknown",
280 SMIv1 => "SMIv1",
281 SMIv2 => "SMIv2",
282 SPPI => "SPPI",
283});
284
285/// Fundamental SMI type that a textual convention or [`Kind::Scalar`]/[`Kind::Column`]
286/// object ultimately resolves to.
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
288#[repr(u8)]
289pub enum BaseType {
290 /// Base type not yet resolved.
291 #[default]
292 Unknown = 0,
293 /// 32-bit signed integer (INTEGER, Integer32).
294 Integer32 = 1,
295 /// 32-bit unsigned integer (Unsigned32).
296 Unsigned32 = 2,
297 /// 32-bit monotonically increasing counter.
298 Counter32 = 3,
299 /// 64-bit monotonically increasing counter.
300 Counter64 = 4,
301 /// 32-bit non-negative integer that can increase or decrease.
302 Gauge32 = 5,
303 /// Hundredths of a second since an epoch.
304 TimeTicks = 6,
305 /// IPv4 address (4 octets).
306 IpAddress = 7,
307 /// Arbitrary binary or text data.
308 OctetString = 8,
309 /// ASN.1 OBJECT IDENTIFIER value.
310 ObjectIdentifier = 9,
311 /// Named bit set.
312 Bits = 10,
313 /// Opaque data (wraps arbitrary ASN.1).
314 Opaque = 11,
315 /// SEQUENCE type used for table row definitions.
316 Sequence = 12,
317 /// 64-bit signed integer (SPPI).
318 Integer64 = 13,
319 /// 64-bit unsigned integer (SPPI).
320 Unsigned64 = 14,
321}
322
323impl_display!(BaseType {
324 Unknown => "unknown",
325 Integer32 => "Integer32",
326 Unsigned32 => "Unsigned32",
327 Counter32 => "Counter32",
328 Counter64 => "Counter64",
329 Gauge32 => "Gauge32",
330 TimeTicks => "TimeTicks",
331 IpAddress => "IpAddress",
332 OctetString => "OCTET STRING",
333 ObjectIdentifier => "OBJECT IDENTIFIER",
334 Bits => "BITS",
335 Opaque => "Opaque",
336 Sequence => "SEQUENCE",
337 Integer64 => "Integer64",
338 Unsigned64 => "Unsigned64",
339});
340
341/// How an INDEX component maps to instance-identifier sub-identifiers (RFC 2578, Section 7.7).
342///
343/// When a table row is identified by its index values, those values are
344/// encoded as OID sub-identifiers appended to the column OID. The
345/// encoding strategy depends on the index object's [`BaseType`] and
346/// constraints:
347///
348/// - Integer types use a single sub-identifier containing the value.
349/// - Fixed-length strings (with a single-value SIZE constraint like
350/// `SIZE (6)`) use one sub-identifier per octet, with no length prefix.
351/// - Variable-length strings are length-prefixed: one sub-identifier
352/// for the length, followed by one per octet.
353/// - The `IMPLIED` keyword omits the length prefix, but can only be
354/// used on the last index component since there is no way to tell
355/// where it ends otherwise.
356///
357/// This encoding matters when constructing or parsing instance OIDs
358/// programmatically (e.g. building an SNMP GET request for a specific
359/// table row).
360#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
361#[repr(u8)]
362pub enum IndexEncoding {
363 /// Encoding not yet determined.
364 #[default]
365 Unknown = 0,
366 /// Single sub-identifier for integer-valued indexes.
367 Integer = 1,
368 /// Fixed number of sub-identifiers (SIZE-constrained OCTET STRING).
369 /// No length prefix; the number of sub-identifiers equals the fixed SIZE.
370 FixedString = 2,
371 /// Length prefix followed by that many sub-identifiers.
372 /// Used for variable-length OCTET STRING and OBJECT IDENTIFIER indexes.
373 LengthPrefixed = 3,
374 /// No length prefix; the index value extends to the end of the OID.
375 /// Only valid for the last index component (uses the `IMPLIED` keyword).
376 Implied = 4,
377 /// Four sub-identifiers encoding an IPv4 address (one per octet).
378 IpAddress = 5,
379}
380
381impl_display!(IndexEncoding {
382 Unknown => "unknown",
383 Integer => "integer",
384 FixedString => "fixed-string",
385 LengthPrefixed => "length-prefixed",
386 Implied => "implied",
387 IpAddress => "ip-address",
388});
389
390/// Records which access keyword was used in the source MIB.
391///
392/// SMIv1 uses `ACCESS`, SMIv2 uses `MAX-ACCESS`, and compliance statements use `MIN-ACCESS`.
393/// The resolved access value is stored separately as [`Access`].
394#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
395#[repr(u8)]
396pub enum AccessKeyword {
397 /// SMIv1 `ACCESS` clause.
398 #[default]
399 Access = 0,
400 /// SMIv2 `MAX-ACCESS` clause.
401 MaxAccess = 1,
402 /// `MIN-ACCESS` clause in MODULE-COMPLIANCE refinements.
403 MinAccess = 2,
404}
405
406impl_display!(AccessKeyword {
407 Access => "ACCESS",
408 MaxAccess => "MAX-ACCESS",
409 MinAccess => "MIN-ACCESS",
410});
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415
416 #[test]
417 fn severity_ordering() {
418 assert!(Severity::Fatal <= Severity::Info);
419 assert!(Severity::Fatal <= Severity::Fatal);
420 assert!(Severity::Info > Severity::Fatal);
421 }
422
423 #[test]
424 fn severity_display() {
425 assert_eq!(Severity::Fatal.to_string(), "fatal");
426 assert_eq!(Severity::Info.to_string(), "info");
427 }
428
429 #[test]
430 fn kind_classification() {
431 assert!(Kind::Scalar.is_object_type());
432 assert!(Kind::Table.is_object_type());
433 assert!(Kind::Row.is_object_type());
434 assert!(Kind::Column.is_object_type());
435 assert!(!Kind::Node.is_object_type());
436 assert!(!Kind::Notification.is_object_type());
437
438 assert!(Kind::Group.is_conformance());
439 assert!(Kind::Compliance.is_conformance());
440 assert!(Kind::Capability.is_conformance());
441 assert!(!Kind::Scalar.is_conformance());
442 }
443
444 #[test]
445 fn status_smiv1() {
446 assert!(Status::Mandatory.is_smiv1());
447 assert!(Status::Optional.is_smiv1());
448 assert!(!Status::Current.is_smiv1());
449 assert!(!Status::Deprecated.is_smiv1());
450 }
451}