use std::fmt;
use crate::mib::Oid;
use crate::types::{Access, BaseType, IndexEncoding, Span};
#[derive(Debug, Clone)]
pub struct ImportSymbol {
pub name: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Import {
pub module: String,
pub symbols: Vec<ImportSymbol>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RangeBound {
Signed(i64),
Unsigned(u64),
Min,
Max,
Raw(String),
}
impl RangeBound {
pub fn as_i64(&self) -> Option<i64> {
match self {
Self::Signed(value) => Some(*value),
Self::Unsigned(value) => i64::try_from(*value).ok(),
Self::Min | Self::Max | Self::Raw(_) => None,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::Signed(value) => u64::try_from(*value).ok(),
Self::Unsigned(value) => Some(*value),
Self::Min | Self::Max | Self::Raw(_) => None,
}
}
pub fn is_concrete(&self) -> bool {
matches!(self, Self::Signed(_) | Self::Unsigned(_))
}
pub(crate) fn cmp_value(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering;
match (self, other) {
(Self::Raw(_), _) | (_, Self::Raw(_)) => None,
(Self::Min, Self::Min) | (Self::Max, Self::Max) => Some(Ordering::Equal),
(Self::Min, _) | (_, Self::Max) => Some(Ordering::Less),
(Self::Max, _) | (_, Self::Min) => Some(Ordering::Greater),
(Self::Signed(left), Self::Signed(right)) => Some(left.cmp(right)),
(Self::Unsigned(left), Self::Unsigned(right)) => Some(left.cmp(right)),
(Self::Signed(left), Self::Unsigned(right)) => Some(if *left < 0 {
Ordering::Less
} else {
(*left as u64).cmp(right)
}),
(Self::Unsigned(left), Self::Signed(right)) => Some(if *right < 0 {
Ordering::Greater
} else {
left.cmp(&(*right as u64))
}),
}
}
}
impl fmt::Display for RangeBound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Signed(value) => write!(f, "{value}"),
Self::Unsigned(value) => write!(f, "{value}"),
Self::Min => f.write_str("MIN"),
Self::Max => f.write_str("MAX"),
Self::Raw(value) => f.write_str(value),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Range {
pub min: RangeBound,
pub max: RangeBound,
pub span: Span,
}
impl Range {
pub fn is_resolved(&self) -> bool {
self.min.is_concrete() && self.max.is_concrete()
}
pub(crate) fn contains_i64(&self, value: i64) -> bool {
let value = RangeBound::Signed(value);
self.min
.cmp_value(&value)
.is_some_and(|ordering| ordering.is_le())
&& self
.max
.cmp_value(&value)
.is_some_and(|ordering| ordering.is_ge())
}
pub(crate) fn contains_u64(&self, value: u64) -> bool {
let value = RangeBound::Unsigned(value);
self.min
.cmp_value(&value)
.is_some_and(|ordering| ordering.is_le())
&& self
.max
.cmp_value(&value)
.is_some_and(|ordering| ordering.is_ge())
}
}
impl fmt::Display for Range {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.min == self.max {
write!(f, "{}", self.min)
} else {
write!(f, "{}..{}", self.min, self.max)
}
}
}
#[derive(Debug, Clone)]
pub struct NamedValue {
pub label: String,
pub value: i64,
pub span: Span,
}
pub(crate) fn find_named_value<'a>(
values: &'a [NamedValue],
label: &str,
) -> Option<&'a NamedValue> {
values.iter().find(|nv| nv.label == label)
}
#[derive(Debug, Clone)]
pub struct Revision {
pub date: String,
pub description: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct IndexEntry {
pub name: String,
pub object: Option<ObjectId>,
pub type_id: Option<TypeId>,
pub implied: bool,
pub encoding: IndexEncoding,
pub span: Span,
}
pub(crate) fn classify_index_encoding(
base: BaseType,
implied: bool,
sizes: &[Range],
) -> IndexEncoding {
match base {
BaseType::Integer32
| BaseType::Unsigned32
| BaseType::Gauge32
| BaseType::TimeTicks
| BaseType::Counter32
| BaseType::Counter64 => IndexEncoding::Integer,
BaseType::IpAddress => IndexEncoding::IpAddress,
BaseType::OctetString | BaseType::Opaque | BaseType::Bits => {
if implied {
IndexEncoding::Implied
} else if is_fixed_size(sizes) {
IndexEncoding::FixedString
} else {
IndexEncoding::LengthPrefixed
}
}
BaseType::ObjectIdentifier => {
if implied {
IndexEncoding::Implied
} else {
IndexEncoding::LengthPrefixed
}
}
_ => IndexEncoding::Unknown,
}
}
pub(crate) fn is_fixed_size(sizes: &[Range]) -> bool {
sizes.len() == 1
&& sizes[0].min == sizes[0].max
&& sizes[0].min.as_u64().is_some_and(|value| value > 0)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum DefValKind {
Unset = 0,
Int = 1,
Uint = 2,
String = 3,
Bytes = 4,
Enum = 5,
Bits = 6,
Oid = 7,
}
impl fmt::Display for DefValKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
DefValKind::Unset => "unset",
DefValKind::Int => "int",
DefValKind::Uint => "uint",
DefValKind::String => "string",
DefValKind::Bytes => "bytes",
DefValKind::Enum => "enum",
DefValKind::Bits => "bits",
DefValKind::Oid => "oid",
})
}
}
#[derive(Debug, Clone)]
pub struct DefVal {
pub(crate) kind: DefValKind,
pub(crate) value: DefValValue,
pub(crate) raw: String,
}
#[derive(Debug, Clone)]
pub enum DefValValue {
None,
Int(i64),
Uint(u64),
String(String),
Bytes(Vec<u8>),
Enum(String),
Bits(Vec<String>),
Oid(Oid),
}
impl DefVal {
pub fn unset() -> Self {
DefVal {
kind: DefValKind::Unset,
value: DefValValue::None,
raw: String::new(),
}
}
pub fn int(v: i64, raw: String) -> Self {
DefVal {
kind: DefValKind::Int,
value: DefValValue::Int(v),
raw,
}
}
pub fn uint(v: u64, raw: String) -> Self {
DefVal {
kind: DefValKind::Uint,
value: DefValValue::Uint(v),
raw,
}
}
pub fn string(v: String, raw: String) -> Self {
DefVal {
kind: DefValKind::String,
value: DefValValue::String(v),
raw,
}
}
pub fn bytes(v: Vec<u8>, raw: String) -> Self {
DefVal {
kind: DefValKind::Bytes,
value: DefValValue::Bytes(v),
raw,
}
}
pub fn enumeration(label: String, raw: String) -> Self {
DefVal {
kind: DefValKind::Enum,
value: DefValValue::Enum(label),
raw,
}
}
pub fn bits(labels: Vec<String>, raw: String) -> Self {
DefVal {
kind: DefValKind::Bits,
value: DefValValue::Bits(labels),
raw,
}
}
pub fn oid(oid: Oid, raw: String) -> Self {
DefVal {
kind: DefValKind::Oid,
value: DefValValue::Oid(oid),
raw,
}
}
pub fn kind(&self) -> DefValKind {
self.kind
}
pub fn raw(&self) -> &str {
&self.raw
}
pub fn value(&self) -> &DefValValue {
&self.value
}
pub fn is_unset(&self) -> bool {
self.kind == DefValKind::Unset
}
}
impl fmt::Display for DefVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.value {
DefValValue::None => Ok(()),
DefValValue::Int(v) => write!(f, "{v}"),
DefValValue::Uint(v) => write!(f, "{v}"),
DefValValue::String(v) => {
write!(f, "\"{}\"", v.replace('"', "\\\""))
}
DefValValue::Bytes(b) => {
write!(f, "0x")?;
for byte in b {
write!(f, "{byte:02X}")?;
}
Ok(())
}
DefValValue::Enum(label) => f.write_str(label),
DefValValue::Bits(labels) => {
if labels.is_empty() {
write!(f, "{{ }}")
} else {
write!(f, "{{ {} }}", labels.join(", "))
}
}
DefValValue::Oid(_) => f.write_str(&self.raw),
}
}
}
#[derive(Debug, Clone)]
pub struct ComplianceModule {
pub module_name: String,
pub mandatory_groups: Vec<String>,
pub groups: Vec<ComplianceGroup>,
pub objects: Vec<ComplianceObject>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct ComplianceGroup {
pub group: String,
pub description: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct ComplianceObject {
pub object: String,
pub syntax: Option<SyntaxConstraints>,
pub write_syntax: Option<SyntaxConstraints>,
pub min_access: Option<Access>,
pub description: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct CapabilitiesModule {
pub module_name: String,
pub includes: Vec<String>,
pub object_variations: Vec<ObjectVariation>,
pub notification_variations: Vec<NotificationVariation>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct ObjectVariation {
pub object: String,
pub syntax: Option<SyntaxConstraints>,
pub write_syntax: Option<SyntaxConstraints>,
pub access: Option<Access>,
pub creation_requires: Vec<String>,
pub def_val: Option<DefVal>,
pub description: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct NotificationVariation {
pub notification: String,
pub access: Option<Access>,
pub description: String,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct SyntaxConstraints {
pub type_id: Option<TypeId>,
pub sizes: Vec<Range>,
pub declared_sizes: Vec<Range>,
pub sizes_constrained: bool,
pub ranges: Vec<Range>,
pub declared_ranges: Vec<Range>,
pub ranges_constrained: bool,
pub enums: Vec<NamedValue>,
pub bits: Vec<NamedValue>,
}
#[derive(Debug, Clone)]
pub struct TrapInfo {
pub enterprise: String,
pub trap_number: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum UnresolvedKind {
Import = 0,
Type = 1,
Oid = 2,
Index = 3,
NotificationObject = 4,
}
impl fmt::Display for UnresolvedKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
UnresolvedKind::Import => "import",
UnresolvedKind::Type => "type",
UnresolvedKind::Oid => "oid",
UnresolvedKind::Index => "index",
UnresolvedKind::NotificationObject => "notification-object",
})
}
}
#[derive(Debug, Clone)]
pub struct UnresolvedRef {
pub kind: UnresolvedKind,
pub symbol: String,
pub module: String,
pub reason: String,
}
#[derive(Debug, Clone)]
pub struct OidRef {
pub name: String,
pub span: Span,
}
macro_rules! define_id {
($(#[$attr:meta])* $name:ident) => {
$(#[$attr])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $name(pub(crate) u32);
impl $name {
pub(crate) fn new(index: u32) -> Self {
Self(index)
}
pub fn index(self) -> u32 {
self.0
}
}
};
}
define_id!(
NodeId
);
define_id!(
ObjectId
);
define_id!(
TypeId
);
define_id!(
ModuleId
);
define_id!(
NotificationId
);
define_id!(
GroupId
);
define_id!(
ComplianceId
);
define_id!(
CapabilityId
);