use std::collections::HashMap;
use std::fmt;
use crate::mib::{Oid, ParseOidError};
use crate::types::{BaseType, Diagnostic, Kind, Severity};
use super::capability::CapabilityData;
use super::compliance::ComplianceData;
use super::group::GroupData;
use super::handle::{
Capability, Compliance, Group, HandleIter, Module, Node, Notification, Object, Type,
};
use super::module::ModuleData;
use super::node::{NodeData, OidTree};
use super::notification::NotificationData;
use super::object::ObjectData;
use super::raw::RawMib;
use super::symbol::Symbol;
use super::typedef::TypeData;
use super::types::*;
pub struct Mib {
pub(crate) tree: OidTree,
pub(crate) objects: Vec<ObjectData>,
pub(crate) types: Vec<TypeData>,
pub(crate) notifications: Vec<NotificationData>,
pub(crate) groups: Vec<GroupData>,
pub(crate) compliances: Vec<ComplianceData>,
pub(crate) capabilities: Vec<CapabilityData>,
pub(crate) modules: Vec<ModuleData>,
pub(crate) module_by_name: HashMap<String, ModuleId>,
pub(crate) name_to_nodes: HashMap<String, Vec<NodeId>>,
pub(crate) type_by_name: HashMap<String, TypeId>,
pub(crate) node_count: usize,
pub(crate) diagnostics: Vec<Diagnostic>,
pub(crate) unresolved: Vec<UnresolvedRef>,
}
impl Mib {
pub(crate) fn new() -> Self {
Self {
tree: OidTree::new(),
objects: Vec::new(),
types: Vec::new(),
notifications: Vec::new(),
groups: Vec::new(),
compliances: Vec::new(),
capabilities: Vec::new(),
modules: Vec::new(),
module_by_name: HashMap::new(),
name_to_nodes: HashMap::new(),
type_by_name: HashMap::new(),
node_count: 0,
diagnostics: Vec::new(),
unresolved: Vec::new(),
}
}
pub(crate) fn tree(&self) -> &OidTree {
&self.tree
}
pub fn raw(&self) -> RawMib<'_> {
RawMib::new(self)
}
pub(crate) fn node_data(&self, id: NodeId) -> &NodeData {
self.tree.get(id)
}
#[must_use]
pub fn root_node(&self) -> Node<'_> {
Node::new(self, self.tree.root())
}
pub(crate) fn object_data(&self, id: ObjectId) -> &ObjectData {
&self.objects[id.0 as usize]
}
#[must_use]
pub(crate) fn type_data(&self, id: TypeId) -> &TypeData {
&self.types[id.0 as usize]
}
pub(crate) fn notification_data(&self, id: NotificationId) -> &NotificationData {
&self.notifications[id.0 as usize]
}
pub(crate) fn group_data(&self, id: GroupId) -> &GroupData {
&self.groups[id.0 as usize]
}
pub(crate) fn compliance_data(&self, id: ComplianceId) -> &ComplianceData {
&self.compliances[id.0 as usize]
}
pub(crate) fn capability_data(&self, id: CapabilityId) -> &CapabilityData {
&self.capabilities[id.0 as usize]
}
pub(crate) fn module_data(&self, id: ModuleId) -> &ModuleData {
&self.modules[id.0 as usize]
}
pub(crate) fn object_mut(&mut self, id: ObjectId) -> &mut ObjectData {
&mut self.objects[id.0 as usize]
}
pub(crate) fn type_mut(&mut self, id: TypeId) -> &mut TypeData {
&mut self.types[id.0 as usize]
}
pub(crate) fn module_mut(&mut self, id: ModuleId) -> &mut ModuleData {
&mut self.modules[id.0 as usize]
}
fn find_in_nodes<T>(&self, name: &str, get: impl Fn(&NodeData) -> Option<T>) -> Option<T> {
for &id in self.name_to_nodes.get(name)? {
if let Some(val) = get(self.tree.get(id)) {
return Some(val);
}
}
None
}
#[must_use]
pub fn nodes_by_name(&self, name: &str) -> &[NodeId] {
self.name_to_nodes
.get(name)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
#[must_use]
pub fn node_by_name(&self, name: &str) -> Option<NodeId> {
let nodes = self.name_to_nodes.get(name)?;
for &id in nodes {
if self.tree.get(id).object.is_some() {
return Some(id);
}
}
for &id in nodes {
if self.tree.get(id).notification.is_some() {
return Some(id);
}
}
nodes.first().copied()
}
#[must_use]
pub fn object_by_name(&self, name: &str) -> Option<ObjectId> {
self.find_in_nodes(name, |n| n.object)
}
#[must_use]
pub fn object(&self, name: &str) -> Option<Object<'_>> {
self.object_by_name(name).map(|id| Object::new(self, id))
}
#[must_use]
pub fn type_by_name(&self, name: &str) -> Option<TypeId> {
self.type_by_name.get(name).copied()
}
#[must_use]
pub fn r#type(&self, name: &str) -> Option<Type<'_>> {
self.type_by_name(name).map(|id| Type::new(self, id))
}
#[must_use]
pub fn notification_by_name(&self, name: &str) -> Option<NotificationId> {
self.find_in_nodes(name, |n| n.notification)
}
#[must_use]
pub fn notification(&self, name: &str) -> Option<Notification<'_>> {
self.notification_by_name(name)
.map(|id| Notification::new(self, id))
}
#[must_use]
pub fn group_by_name(&self, name: &str) -> Option<GroupId> {
self.find_in_nodes(name, |n| n.group)
}
#[must_use]
pub fn group(&self, name: &str) -> Option<Group<'_>> {
self.group_by_name(name).map(|id| Group::new(self, id))
}
#[must_use]
pub fn compliance_by_name(&self, name: &str) -> Option<ComplianceId> {
self.find_in_nodes(name, |n| n.compliance)
}
#[must_use]
pub fn compliance(&self, name: &str) -> Option<Compliance<'_>> {
self.compliance_by_name(name)
.map(|id| Compliance::new(self, id))
}
#[must_use]
pub fn capability_by_name(&self, name: &str) -> Option<CapabilityId> {
self.find_in_nodes(name, |n| n.capability)
}
#[must_use]
pub fn capability(&self, name: &str) -> Option<Capability<'_>> {
self.capability_by_name(name)
.map(|id| Capability::new(self, id))
}
#[must_use]
pub fn module_by_name(&self, name: &str) -> Option<ModuleId> {
self.module_by_name.get(name).copied()
}
#[must_use]
pub fn module(&self, name: &str) -> Option<Module<'_>> {
self.module_by_name(name).map(|id| Module::new(self, id))
}
#[must_use]
pub fn symbol_by_name(&self, name: &str) -> Option<Symbol> {
if let Some(nodes) = self.name_to_nodes.get(name) {
let mut notification = None;
let mut group = None;
let mut compliance = None;
let mut capability = None;
let mut node = None;
for &id in nodes {
let entry = self.tree.get(id);
node.get_or_insert(id);
if let Some(object) = entry.object {
return Some(Symbol::Object(object));
}
notification = notification.or(entry.notification);
group = group.or(entry.group);
compliance = compliance.or(entry.compliance);
capability = capability.or(entry.capability);
}
if let Some(id) = notification {
return Some(Symbol::Notification(id));
}
if let Some(id) = group {
return Some(Symbol::Group(id));
}
if let Some(id) = compliance {
return Some(Symbol::Compliance(id));
}
if let Some(id) = capability {
return Some(Symbol::Capability(id));
}
if let Some(id) = node {
return Some(Symbol::Node(id));
}
}
if let Some(&id) = self.type_by_name.get(name) {
return Some(Symbol::Type(id));
}
None
}
#[must_use]
pub fn node_by_oid(&self, oid: &Oid) -> Option<NodeId> {
let (id, exact) = self.tree.walk_oid(self.tree.root(), oid);
if exact { Some(id) } else { None }
}
#[must_use]
pub fn node(&self, name: &str) -> Option<Node<'_>> {
self.node_by_name(name).map(|id| Node::new(self, id))
}
#[must_use]
pub fn exact_node_by_oid(&self, oid: &Oid) -> Option<Node<'_>> {
self.node_by_oid(oid).map(|id| Node::new(self, id))
}
#[must_use]
pub fn longest_prefix_by_oid(&self, oid: &Oid) -> NodeId {
self.tree.longest_prefix(oid)
}
#[must_use]
pub fn lookup_oid(&self, oid: &Oid) -> Node<'_> {
Node::new(self, self.longest_prefix_by_oid(oid))
}
#[must_use]
pub fn lookup_instance(&self, oid: &Oid) -> OidLookup<'_> {
let id = self.longest_prefix_by_oid(oid);
let node_oid = self.tree.oid_of(id);
let suffix = oid[node_oid.len()..].to_vec();
OidLookup {
node: Node::new(self, id),
suffix,
}
}
pub fn subtree(&self, id: NodeId) -> super::node::SubtreeIter<'_> {
self.tree.subtree(id)
}
#[must_use]
pub fn longest_prefix_from(&self, start: NodeId, oid: &Oid) -> NodeId {
self.tree.longest_prefix_from(start, oid)
}
pub(crate) fn effective_module(&self, id: NodeId) -> Option<ModuleId> {
self.tree.get(id).module
}
pub fn format_oid(&self, oid: &Oid) -> String {
if oid.is_empty() {
return String::new();
}
let lookup = self.lookup_instance(oid);
let matched = self.tree.get(lookup.node.id);
if matched.name.is_empty() {
return oid.to_string();
}
let mut result = String::new();
if let Some(mod_id) = self.effective_module(lookup.node.id) {
result.push_str(&self.modules[mod_id.0 as usize].name);
result.push_str("::");
}
result.push_str(&matched.name);
for arc in lookup.suffix() {
result.push('.');
result.push_str(&arc.to_string());
}
result
}
pub fn resolve_node(&self, query: &str) -> Option<Node<'_>> {
self.resolve(query).map(|id| Node::new(self, id))
}
pub(crate) fn resolve(&self, query: &str) -> Option<NodeId> {
let q = query.strip_prefix('.').unwrap_or(query);
if q.starts_with(|c: char| c.is_ascii_digit()) {
let oid: Oid = q.parse().ok()?;
return Some(self.longest_prefix_by_oid(&oid));
}
self.resolve_oid(query)
.ok()
.map(|oid| self.longest_prefix_by_oid(&oid))
}
pub fn resolve_oid(&self, query: &str) -> Result<Oid, ResolveOidError> {
if query.is_empty() {
return Err(ResolveOidError::EmptyQuery);
}
let q = query.strip_prefix('.').unwrap_or(query);
if q.starts_with(|c: char| c.is_ascii_digit()) {
return q.parse::<Oid>().map_err(ResolveOidError::InvalidOid);
}
if let Some((mod_name, rest)) = query.split_once("::") {
let (name, suffix) = split_name_suffix(rest);
let mod_id = self
.module_by_name
.get(mod_name)
.ok_or_else(|| ResolveOidError::ModuleNotFound(mod_name.to_string()))?;
let node_id = self.modules[mod_id.0 as usize]
.node_by_name(name)
.ok_or_else(|| ResolveOidError::QualifiedNodeNotFound {
module: mod_name.to_string(),
name: name.to_string(),
})?;
let base = self.tree.oid_of(node_id).clone();
return append_suffix(base, suffix);
}
let (name, suffix) = split_name_suffix(query);
let node_id = self
.node_by_name(name)
.ok_or_else(|| ResolveOidError::NodeNotFound(name.to_string()))?;
let base = self.tree.oid_of(node_id).clone();
append_suffix(base, suffix)
}
pub fn all_symbols(&self) -> Vec<Symbol> {
let mut result = Vec::new();
for module in &self.modules {
result.extend(module.definitions());
}
result
}
pub fn available_symbols(&self, mod_id: ModuleId) -> Vec<Symbol> {
let module = self.module_data(mod_id);
let mut result = Vec::new();
let mut seen = std::collections::HashSet::new();
for sym in module.definitions() {
seen.insert(sym.name(self).to_string());
result.push(sym);
}
for imp in &module.imports {
for is in &imp.symbols {
if seen.contains(&is.name) {
continue;
}
seen.insert(is.name.clone());
let Some(&source_mod_id) = module.resolved_imports.get(&is.name) else {
continue;
};
let source = self.module_data(source_mod_id);
if let Some(sym) = source.symbol(&is.name) {
result.push(sym);
}
}
}
result
}
pub fn modules(&self) -> HandleIter<'_, Module<'_>, impl Iterator<Item = ModuleId>> {
HandleIter::new(
self,
(0..self.modules.len()).map(|i| ModuleId::new(i as u32)),
)
}
pub fn user_modules(&self) -> impl Iterator<Item = Module<'_>> + '_ {
self.modules
.iter()
.enumerate()
.filter(|(_, m)| !m.is_base())
.map(|(i, _)| Module::new(self, ModuleId::new(i as u32)))
}
pub fn objects(&self) -> HandleIter<'_, Object<'_>, impl Iterator<Item = ObjectId>> {
HandleIter::new(
self,
(0..self.objects.len()).map(|i| ObjectId::new(i as u32)),
)
}
pub fn types(&self) -> HandleIter<'_, Type<'_>, impl Iterator<Item = TypeId>> {
HandleIter::new(self, (0..self.types.len()).map(|i| TypeId::new(i as u32)))
}
pub fn nodes(&self) -> HandleIter<'_, Node<'_>, impl Iterator<Item = NodeId>> {
HandleIter::new(self, self.tree.all_nodes())
}
pub fn notifications(
&self,
) -> HandleIter<'_, Notification<'_>, impl Iterator<Item = NotificationId>> {
HandleIter::new(
self,
(0..self.notifications.len()).map(|i| NotificationId::new(i as u32)),
)
}
pub fn groups(&self) -> HandleIter<'_, Group<'_>, impl Iterator<Item = GroupId>> {
HandleIter::new(self, (0..self.groups.len()).map(|i| GroupId::new(i as u32)))
}
pub fn compliances(
&self,
) -> HandleIter<'_, Compliance<'_>, impl Iterator<Item = ComplianceId>> {
HandleIter::new(
self,
(0..self.compliances.len()).map(|i| ComplianceId::new(i as u32)),
)
}
pub fn capabilities(
&self,
) -> HandleIter<'_, Capability<'_>, impl Iterator<Item = CapabilityId>> {
HandleIter::new(
self,
(0..self.capabilities.len()).map(|i| CapabilityId::new(i as u32)),
)
}
pub fn node_by_id(&self, id: NodeId) -> Node<'_> {
Node::new(self, id)
}
pub fn object_by_id(&self, id: ObjectId) -> Object<'_> {
Object::new(self, id)
}
pub fn type_by_id(&self, id: TypeId) -> Type<'_> {
Type::new(self, id)
}
pub fn module_by_id(&self, id: ModuleId) -> Module<'_> {
Module::new(self, id)
}
pub fn notification_by_id(&self, id: NotificationId) -> Notification<'_> {
Notification::new(self, id)
}
pub fn group_by_id(&self, id: GroupId) -> Group<'_> {
Group::new(self, id)
}
pub fn compliance_by_id(&self, id: ComplianceId) -> Compliance<'_> {
Compliance::new(self, id)
}
pub fn capability_by_id(&self, id: CapabilityId) -> Capability<'_> {
Capability::new(self, id)
}
pub(crate) fn modules_slice(&self) -> &[ModuleData] {
&self.modules
}
pub(crate) fn objects_slice(&self) -> &[ObjectData] {
&self.objects
}
pub(crate) fn types_slice(&self) -> &[TypeData] {
&self.types
}
pub(crate) fn notifications_slice(&self) -> &[NotificationData] {
&self.notifications
}
pub(crate) fn groups_slice(&self) -> &[GroupData] {
&self.groups
}
pub(crate) fn compliances_slice(&self) -> &[ComplianceData] {
&self.compliances
}
pub(crate) fn capabilities_slice(&self) -> &[CapabilityData] {
&self.capabilities
}
pub fn modules_defining(&self, name: &str) -> Vec<ModuleId> {
self.modules
.iter()
.enumerate()
.filter(|(_, m)| !m.is_base && m.defines_symbol(name))
.map(|(i, _)| ModuleId::new(i as u32))
.collect()
}
pub fn modules_importing(&self, name: &str) -> Vec<ModuleId> {
self.modules
.iter()
.enumerate()
.filter(|(_, m)| !m.is_base && m.imports_symbol(name))
.map(|(i, _)| ModuleId::new(i as u32))
.collect()
}
pub fn objects_by_kind(&self, kind: Kind) -> Vec<ObjectId> {
self.objects
.iter()
.enumerate()
.filter(|(_, obj)| {
obj.entity
.node
.is_some_and(|id| self.tree.get(id).kind == kind)
})
.map(|(i, _)| ObjectId::new(i as u32))
.collect()
}
pub fn tables(&self) -> impl Iterator<Item = Object<'_>> + '_ {
self.objects_with_kind(Kind::Table)
}
pub fn scalars(&self) -> impl Iterator<Item = Object<'_>> + '_ {
self.objects_with_kind(Kind::Scalar)
}
pub fn columns(&self) -> impl Iterator<Item = Object<'_>> + '_ {
self.objects_with_kind(Kind::Column)
}
pub fn rows(&self) -> impl Iterator<Item = Object<'_>> + '_ {
self.objects_with_kind(Kind::Row)
}
pub fn objects_by_type_name(&self, type_name: &str) -> Vec<ObjectId> {
self.objects
.iter()
.enumerate()
.filter(|(_, obj)| {
obj.typ
.is_some_and(|id| self.types[id.0 as usize].name == type_name)
})
.map(|(i, _)| ObjectId::new(i as u32))
.collect()
}
pub fn objects_by_base_type(&self, base: BaseType) -> Vec<ObjectId> {
self.objects
.iter()
.enumerate()
.filter(|(_, obj)| {
obj.typ
.is_some_and(|id| self.types[id.0 as usize].effective_base(&self.types) == base)
})
.map(|(i, _)| ObjectId::new(i as u32))
.collect()
}
pub fn object_table(&self, id: ObjectId) -> Option<ObjectId> {
let node_id = self.object_data(id).node()?;
let node = self.tree.get(node_id);
match node.kind {
Kind::Table => Some(id),
Kind::Row => {
let parent = self.tree.get(node.parent?);
parent.object
}
Kind::Column => {
let parent = self.tree.get(node.parent?);
let grandparent = self.tree.get(parent.parent?);
grandparent.object
}
_ => None,
}
}
pub fn object_row(&self, id: ObjectId) -> Option<ObjectId> {
let node_id = self.object_data(id).node()?;
let node = self.tree.get(node_id);
match node.kind {
Kind::Table => {
for &child_id in node.children.values() {
let child = self.tree.get(child_id);
if child.kind == Kind::Row {
return child.object;
}
}
None
}
Kind::Row => Some(id),
Kind::Column => {
let parent = self.tree.get(node.parent?);
parent.object
}
_ => None,
}
}
pub fn object_columns(&self, id: ObjectId) -> Vec<ObjectId> {
let Some(node_id) = self.object_data(id).node() else {
return Vec::new();
};
let node = self.tree.get(node_id);
let row_node = match node.kind {
Kind::Table => {
let mut found = None;
for &child_id in node.children.values() {
if self.tree.get(child_id).kind == Kind::Row {
found = Some(child_id);
break;
}
}
match found {
Some(id) => self.tree.get(id),
None => return Vec::new(),
}
}
Kind::Row => node,
_ => return Vec::new(),
};
let mut cols = Vec::new();
for &child_id in row_node.children.values() {
let child = self.tree.get(child_id);
if let Some(obj_id) = child.object.filter(|_| child.kind == Kind::Column) {
cols.push(obj_id);
}
}
cols
}
pub fn effective_indexes(&self, id: ObjectId) -> Vec<IndexEntry> {
let mut visited = Vec::new();
self.effective_indexes_inner(id, &mut visited)
}
pub(crate) fn effective_indexes_source(&self, id: ObjectId) -> Option<ObjectId> {
let mut visited = Vec::new();
self.effective_indexes_source_inner(id, &mut visited)
}
fn effective_indexes_inner(
&self,
id: ObjectId,
visited: &mut Vec<ObjectId>,
) -> Vec<IndexEntry> {
let obj = self.object_data(id);
let Some(node_id) = obj.node() else {
return Vec::new();
};
let kind = self.tree.get(node_id).kind;
if kind == Kind::Column {
if let Some(row_id) = self.object_row(id) {
return self.effective_indexes_inner(row_id, visited);
}
return Vec::new();
}
if kind != Kind::Row {
return Vec::new();
}
if !obj.index.is_empty() {
return obj.index.clone();
}
if let Some(aug_id) = obj.augments {
if visited.contains(&id) {
return Vec::new();
}
visited.push(id);
return self.effective_indexes_inner(aug_id, visited);
}
Vec::new()
}
fn effective_indexes_source_inner(
&self,
id: ObjectId,
visited: &mut Vec<ObjectId>,
) -> Option<ObjectId> {
let obj = self.object_data(id);
let node_id = obj.node()?;
let kind = self.tree.get(node_id).kind;
if kind == Kind::Column {
let row_id = self.object_row(id)?;
return self.effective_indexes_source_inner(row_id, visited);
}
if kind != Kind::Row {
return None;
}
if !obj.index.is_empty() {
return Some(id);
}
let aug_id = obj.augments?;
if visited.contains(&id) {
return None;
}
visited.push(id);
self.effective_indexes_source_inner(aug_id, visited)
}
pub fn is_table(&self, id: ObjectId) -> bool {
self.object_kind(id) == Kind::Table
}
pub fn is_row(&self, id: ObjectId) -> bool {
self.object_kind(id) == Kind::Row
}
pub fn is_column(&self, id: ObjectId) -> bool {
self.object_kind(id) == Kind::Column
}
pub fn is_scalar(&self, id: ObjectId) -> bool {
self.object_kind(id) == Kind::Scalar
}
pub fn is_index(&self, id: ObjectId) -> bool {
if self.object_kind(id) != Kind::Column {
return false;
}
self.effective_indexes(id)
.iter()
.any(|idx| idx.object == Some(id))
}
fn objects_with_kind(&self, kind: Kind) -> impl Iterator<Item = Object<'_>> + '_ {
self.objects.iter().enumerate().filter_map(move |(i, obj)| {
let node_id = obj.entity.node?;
if self.tree.get(node_id).kind == kind {
Some(Object::new(self, ObjectId::new(i as u32)))
} else {
None
}
})
}
fn object_kind(&self, id: ObjectId) -> Kind {
match self.object_data(id).node() {
Some(node_id) => self.tree.get(node_id).kind,
None => Kind::Unknown,
}
}
pub fn node_count(&self) -> usize {
self.node_count
}
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
pub fn unresolved(&self) -> &[UnresolvedRef] {
&self.unresolved
}
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity.at_least(Severity::Error))
}
pub(crate) fn set_node_count(&mut self, n: usize) {
self.node_count = n;
}
pub(crate) fn add_module(&mut self, data: ModuleData) -> ModuleId {
let id = ModuleId::new(self.modules.len() as u32);
if !data.name.is_empty() {
self.module_by_name.insert(data.name.clone(), id);
}
self.modules.push(data);
id
}
pub(crate) fn add_object(&mut self, data: ObjectData) -> ObjectId {
let id = ObjectId::new(self.objects.len() as u32);
self.objects.push(data);
id
}
pub(crate) fn add_type(&mut self, data: TypeData) -> TypeId {
let id = TypeId::new(self.types.len() as u32);
if !data.name.is_empty() && !self.type_by_name.contains_key(&data.name) {
self.type_by_name.insert(data.name.clone(), id);
}
self.types.push(data);
id
}
pub(crate) fn add_notification(&mut self, data: NotificationData) -> NotificationId {
let id = NotificationId::new(self.notifications.len() as u32);
self.notifications.push(data);
id
}
pub(crate) fn add_group(&mut self, data: GroupData) -> GroupId {
let id = GroupId::new(self.groups.len() as u32);
self.groups.push(data);
id
}
pub(crate) fn add_compliance(&mut self, data: ComplianceData) -> ComplianceId {
let id = ComplianceId::new(self.compliances.len() as u32);
self.compliances.push(data);
id
}
pub(crate) fn add_capability(&mut self, data: CapabilityData) -> CapabilityId {
let id = CapabilityId::new(self.capabilities.len() as u32);
self.capabilities.push(data);
id
}
pub(crate) fn register_node(&mut self, name: &str, id: NodeId) {
if !name.is_empty() {
self.name_to_nodes
.entry(name.to_string())
.or_default()
.push(id);
}
}
pub(crate) fn add_diagnostic(&mut self, d: Diagnostic) {
self.diagnostics.push(d);
}
pub(crate) fn add_unresolved(&mut self, r: UnresolvedRef) {
self.unresolved.push(r);
}
}
impl Default for Mib {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Mib {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Mib")
.field("modules", &self.modules.len())
.field("objects", &self.objects.len())
.field("types", &self.types.len())
.field("notifications", &self.notifications.len())
.field("node_count", &self.node_count)
.finish()
}
}
fn split_name_suffix(s: &str) -> (&str, &str) {
match s.find('.') {
Some(i) => (&s[..i], &s[i..]),
None => (s, ""),
}
}
fn append_suffix(base: Oid, suffix: &str) -> Result<Oid, ResolveOidError> {
if suffix.is_empty() {
return Ok(base);
}
let extra: Oid = suffix
.parse()
.map_err(|source| ResolveOidError::InvalidSuffix {
suffix: suffix.to_string(),
source,
})?;
Ok(base.child_oid(&extra))
}
pub struct OidLookup<'a> {
node: Node<'a>,
suffix: Vec<u32>,
}
impl<'a> OidLookup<'a> {
#[must_use]
pub fn node(&self) -> Node<'a> {
self.node
}
#[must_use]
pub fn suffix(&self) -> &[u32] {
&self.suffix
}
#[must_use]
pub fn decode_indexes(&self) -> Vec<super::index::DecodedIndex> {
let Some(obj) = self.node.object() else {
return Vec::new();
};
let mut indexes = obj.effective_indexes().peekable();
if indexes.peek().is_none() {
return Vec::new();
}
super::index::decode_suffix(indexes, &self.suffix)
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum ResolveOidError {
#[error("empty query")]
EmptyQuery,
#[error("invalid OID: {0}")]
InvalidOid(#[source] ParseOidError),
#[error("module not found: {0}")]
ModuleNotFound(String),
#[error("node not found: {0}")]
NodeNotFound(String),
#[error("node not found: {module}::{name}")]
QualifiedNodeNotFound {
module: String,
name: String,
},
#[error("invalid instance suffix {suffix:?}: {source}")]
InvalidSuffix {
suffix: String,
#[source]
source: ParseOidError,
},
}
impl Oid {
fn child_oid(&self, suffix: &Oid) -> Oid {
let mut arcs = Vec::with_capacity(self.len() + suffix.len());
arcs.extend_from_slice(self);
arcs.extend_from_slice(suffix);
Oid::from(arcs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mib::module::ModuleData;
use crate::mib::object::ObjectData;
use crate::mib::typedef::TypeData;
fn make_mib_with_two_modules() -> Mib {
let mut mib = Mib::new();
let mut mod_a = ModuleData::new("MOD-A".to_string());
let obj_data = ObjectData::new("objA".to_string());
let obj_id = mib.add_object(obj_data);
mod_a.add_object("objA", obj_id);
let type_data = TypeData::new("TypeA".to_string());
let type_id = mib.add_type(type_data);
mod_a.add_type("TypeA", type_id);
let _mod_a_id = mib.add_module(mod_a);
let mut mod_b = ModuleData::new("MOD-B".to_string());
let obj_data2 = ObjectData::new("objB".to_string());
let obj_id2 = mib.add_object(obj_data2);
mod_b.add_object("objB", obj_id2);
let _mod_b_id = mib.add_module(mod_b);
mib
}
#[test]
fn all_symbols_across_modules() {
let mib = make_mib_with_two_modules();
let syms = mib.all_symbols();
let names: Vec<&str> = syms.iter().map(|s| s.name(&mib)).collect();
assert!(names.contains(&"objA"));
assert!(names.contains(&"TypeA"));
assert!(names.contains(&"objB"));
assert_eq!(names.len(), 3);
}
#[test]
fn available_symbols_own_only() {
let mib = make_mib_with_two_modules();
let mod_a_id = *mib.module_by_name.get("MOD-A").unwrap();
let syms = mib.available_symbols(mod_a_id);
let names: Vec<&str> = syms.iter().map(|s| s.name(&mib)).collect();
assert!(names.contains(&"objA"));
assert!(names.contains(&"TypeA"));
assert!(!names.contains(&"objB"));
}
#[test]
fn available_symbols_with_imports() {
let mut mib = Mib::new();
let mut source_mod = ModuleData::new("SOURCE-MIB".to_string());
let obj_data = ObjectData::new("srcObj".to_string());
let obj_id = mib.add_object(obj_data);
source_mod.add_object("srcObj", obj_id);
let source_mod_id = mib.add_module(source_mod);
let mut consumer = ModuleData::new("CONSUMER-MIB".to_string());
let own_type = TypeData::new("OwnType".to_string());
let own_type_id = mib.add_type(own_type);
consumer.add_type("OwnType", own_type_id);
consumer.imports.push(crate::mib::types::Import {
module: "SOURCE-MIB".to_string(),
symbols: vec![crate::mib::types::ImportSymbol {
name: "srcObj".to_string(),
span: crate::types::Span::SYNTHETIC,
}],
});
consumer
.resolved_imports
.insert("srcObj".to_string(), source_mod_id);
let consumer_id = mib.add_module(consumer);
let syms = mib.available_symbols(consumer_id);
let names: Vec<&str> = syms.iter().map(|s| s.name(&mib)).collect();
assert_eq!(names, vec!["OwnType", "srcObj"]);
}
#[test]
fn available_symbols_dedup_own_over_import() {
let mut mib = Mib::new();
let mut source_mod = ModuleData::new("SOURCE-MIB".to_string());
let src_type = TypeData::new("shared".to_string());
let src_type_id = mib.add_type(src_type);
source_mod.add_type("shared", src_type_id);
let source_mod_id = mib.add_module(source_mod);
let mut consumer = ModuleData::new("CONSUMER-MIB".to_string());
let own_type = TypeData::new("shared".to_string());
let own_type_id = mib.add_type(own_type);
consumer.add_type("shared", own_type_id);
consumer.imports.push(crate::mib::types::Import {
module: "SOURCE-MIB".to_string(),
symbols: vec![crate::mib::types::ImportSymbol {
name: "shared".to_string(),
span: crate::types::Span::SYNTHETIC,
}],
});
consumer
.resolved_imports
.insert("shared".to_string(), source_mod_id);
let consumer_id = mib.add_module(consumer);
let syms = mib.available_symbols(consumer_id);
let names: Vec<&str> = syms.iter().map(|s| s.name(&mib)).collect();
assert_eq!(names, vec!["shared"]);
}
}