1use std::collections::{HashMap, HashSet};
10
11use crate::mib::Oid;
12use crate::types::Language;
13
14use super::symbol::Symbol;
15use super::types::*;
16
17pub struct ModuleData {
23 pub(crate) name: String,
24 pub(crate) language: Language,
25 pub(crate) source_path: String,
26 pub(crate) is_base: bool,
27 pub(crate) oid: Option<Oid>,
28 pub(crate) organization: String,
29 pub(crate) contact_info: String,
30 pub(crate) description: String,
31 pub(crate) last_updated: String,
32 pub(crate) revisions: Vec<Revision>,
33 pub(crate) imports: Vec<Import>,
34
35 pub(crate) objects: Vec<ObjectId>,
36 pub(crate) types: Vec<TypeId>,
37 pub(crate) notifications: Vec<NotificationId>,
38 pub(crate) groups: Vec<GroupId>,
39 pub(crate) compliances: Vec<ComplianceId>,
40 pub(crate) capabilities: Vec<CapabilityId>,
41 pub(crate) nodes: Vec<NodeId>,
42
43 pub(crate) line_table: Vec<usize>,
44
45 pub(crate) used_import_names: HashSet<String>,
46 pub(crate) resolved_imports: HashMap<String, ModuleId>,
47
48 pub(crate) objects_by_name: HashMap<String, ObjectId>,
49 pub(crate) types_by_name: HashMap<String, TypeId>,
50 pub(crate) notifications_by_name: HashMap<String, NotificationId>,
51 pub(crate) groups_by_name: HashMap<String, GroupId>,
52 pub(crate) compliances_by_name: HashMap<String, ComplianceId>,
53 pub(crate) capabilities_by_name: HashMap<String, CapabilityId>,
54 pub(crate) nodes_by_name: HashMap<String, NodeId>,
55}
56
57impl ModuleData {
58 pub(crate) fn new(name: String) -> Self {
59 Self {
60 name,
61 language: Language::Unknown,
62 source_path: String::new(),
63 is_base: false,
64 oid: None,
65 organization: String::new(),
66 contact_info: String::new(),
67 description: String::new(),
68 last_updated: String::new(),
69 revisions: Vec::new(),
70 imports: Vec::new(),
71 objects: Vec::new(),
72 types: Vec::new(),
73 notifications: Vec::new(),
74 groups: Vec::new(),
75 compliances: Vec::new(),
76 capabilities: Vec::new(),
77 nodes: Vec::new(),
78 line_table: Vec::new(),
79 used_import_names: HashSet::new(),
80 resolved_imports: HashMap::new(),
81 objects_by_name: HashMap::new(),
82 types_by_name: HashMap::new(),
83 notifications_by_name: HashMap::new(),
84 groups_by_name: HashMap::new(),
85 compliances_by_name: HashMap::new(),
86 capabilities_by_name: HashMap::new(),
87 nodes_by_name: HashMap::new(),
88 }
89 }
90
91 pub fn name(&self) -> &str {
93 &self.name
94 }
95
96 pub fn language(&self) -> Language {
98 self.language
99 }
100
101 pub fn source_path(&self) -> &str {
103 &self.source_path
104 }
105
106 pub fn is_base(&self) -> bool {
110 self.is_base
111 }
112
113 pub fn line_col(&self, offset: crate::types::ByteOffset) -> (usize, usize) {
115 crate::types::line_col_from_table(&self.line_table, offset)
116 }
117
118 pub fn oid(&self) -> Option<&Oid> {
120 self.oid.as_ref()
121 }
122
123 pub fn organization(&self) -> &str {
125 &self.organization
126 }
127
128 pub fn contact_info(&self) -> &str {
130 &self.contact_info
131 }
132
133 pub fn description(&self) -> &str {
135 &self.description
136 }
137
138 pub fn last_updated(&self) -> &str {
140 &self.last_updated
141 }
142
143 pub fn revisions(&self) -> &[Revision] {
145 &self.revisions
146 }
147
148 pub fn imports(&self) -> &[Import] {
150 &self.imports
151 }
152
153 pub fn objects(&self) -> &[ObjectId] {
155 &self.objects
156 }
157
158 pub fn types(&self) -> &[TypeId] {
160 &self.types
161 }
162
163 pub fn notifications(&self) -> &[NotificationId] {
165 &self.notifications
166 }
167
168 pub fn groups(&self) -> &[GroupId] {
170 &self.groups
171 }
172
173 pub fn compliances(&self) -> &[ComplianceId] {
175 &self.compliances
176 }
177
178 pub fn capabilities(&self) -> &[CapabilityId] {
180 &self.capabilities
181 }
182
183 pub fn nodes(&self) -> &[NodeId] {
185 &self.nodes
186 }
187
188 pub fn object_by_name(&self, name: &str) -> Option<ObjectId> {
190 self.objects_by_name.get(name).copied()
191 }
192
193 pub fn type_by_name(&self, name: &str) -> Option<TypeId> {
195 self.types_by_name.get(name).copied()
196 }
197
198 pub fn notification_by_name(&self, name: &str) -> Option<NotificationId> {
200 self.notifications_by_name.get(name).copied()
201 }
202
203 pub fn group_by_name(&self, name: &str) -> Option<GroupId> {
205 self.groups_by_name.get(name).copied()
206 }
207
208 pub fn compliance_by_name(&self, name: &str) -> Option<ComplianceId> {
210 self.compliances_by_name.get(name).copied()
211 }
212
213 pub fn capability_by_name(&self, name: &str) -> Option<CapabilityId> {
215 self.capabilities_by_name.get(name).copied()
216 }
217
218 pub fn node_by_name(&self, name: &str) -> Option<NodeId> {
220 self.nodes_by_name.get(name).copied()
221 }
222
223 pub fn symbol(&self, name: &str) -> Option<Symbol> {
226 if let Some(&id) = self.objects_by_name.get(name) {
227 return Some(Symbol::Object(id));
228 }
229 if let Some(&id) = self.types_by_name.get(name) {
230 return Some(Symbol::Type(id));
231 }
232 if let Some(&id) = self.notifications_by_name.get(name) {
233 return Some(Symbol::Notification(id));
234 }
235 if let Some(&id) = self.groups_by_name.get(name) {
236 return Some(Symbol::Group(id));
237 }
238 if let Some(&id) = self.compliances_by_name.get(name) {
239 return Some(Symbol::Compliance(id));
240 }
241 if let Some(&id) = self.capabilities_by_name.get(name) {
242 return Some(Symbol::Capability(id));
243 }
244 if let Some(&id) = self.nodes_by_name.get(name) {
245 return Some(Symbol::Node(id));
246 }
247 None
248 }
249
250 pub fn defines_symbol(&self, name: &str) -> bool {
252 self.symbol(name).is_some()
253 }
254
255 pub fn imports_symbol(&self, name: &str) -> bool {
257 self.imports
258 .iter()
259 .any(|imp| imp.symbols.iter().any(|s| s.name == name))
260 }
261
262 pub fn is_import_used(&self, name: &str) -> bool {
264 self.used_import_names.contains(name)
265 }
266
267 pub fn import_source(&self, name: &str) -> Option<ModuleId> {
269 self.resolved_imports.get(name).copied()
270 }
271
272 pub(crate) fn add_object(&mut self, name: impl Into<String>, id: ObjectId) {
275 self.objects.push(id);
276 self.objects_by_name.entry(name.into()).or_insert(id);
277 }
278
279 pub(crate) fn add_type(&mut self, name: impl Into<String>, id: TypeId) {
280 self.types.push(id);
281 self.types_by_name.entry(name.into()).or_insert(id);
282 }
283
284 pub(crate) fn add_notification(&mut self, name: impl Into<String>, id: NotificationId) {
285 self.notifications.push(id);
286 self.notifications_by_name.entry(name.into()).or_insert(id);
287 }
288
289 pub(crate) fn add_group(&mut self, name: impl Into<String>, id: GroupId) {
290 self.groups.push(id);
291 self.groups_by_name.entry(name.into()).or_insert(id);
292 }
293
294 pub(crate) fn add_compliance(&mut self, name: impl Into<String>, id: ComplianceId) {
295 self.compliances.push(id);
296 self.compliances_by_name.entry(name.into()).or_insert(id);
297 }
298
299 pub(crate) fn add_capability(&mut self, name: impl Into<String>, id: CapabilityId) {
300 self.capabilities.push(id);
301 self.capabilities_by_name.entry(name.into()).or_insert(id);
302 }
303
304 pub(crate) fn add_node(&mut self, name: impl Into<String>, id: NodeId) {
305 self.nodes.push(id);
306 self.nodes_by_name.entry(name.into()).or_insert(id);
307 }
308
309 pub fn definitions(&self) -> impl Iterator<Item = Symbol> + '_ {
315 let covered_node_ids: HashSet<NodeId> = self
317 .nodes_by_name
318 .iter()
319 .filter_map(|(name, &id)| {
320 (self.objects_by_name.contains_key(name)
321 || self.notifications_by_name.contains_key(name)
322 || self.groups_by_name.contains_key(name)
323 || self.compliances_by_name.contains_key(name)
324 || self.capabilities_by_name.contains_key(name))
325 .then_some(id)
326 })
327 .collect();
328
329 self.objects
330 .iter()
331 .map(|&id| Symbol::Object(id))
332 .chain(self.types.iter().map(|&id| Symbol::Type(id)))
333 .chain(
334 self.notifications
335 .iter()
336 .map(|&id| Symbol::Notification(id)),
337 )
338 .chain(self.groups.iter().map(|&id| Symbol::Group(id)))
339 .chain(self.compliances.iter().map(|&id| Symbol::Compliance(id)))
340 .chain(self.capabilities.iter().map(|&id| Symbol::Capability(id)))
341 .chain(
342 self.nodes
343 .iter()
344 .filter(move |id| !covered_node_ids.contains(id))
345 .map(|&id| Symbol::Node(id)),
346 )
347 }
348}
349
350impl std::fmt::Debug for ModuleData {
351 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352 f.debug_struct("ModuleData")
353 .field("name", &self.name)
354 .field("language", &self.language)
355 .field("is_base", &self.is_base)
356 .finish()
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn definitions_include_only_plain_nodes() {
366 let mut module = ModuleData::new("TEST-MIB".to_string());
367
368 let object_id = ObjectId::new(0);
369 let object_node = NodeId::new(10);
370 let plain_node = NodeId::new(11);
371
372 module.add_object("ifIndex", object_id);
373 module.add_node("ifIndex", object_node);
374 module.add_node("internet", plain_node);
375
376 let defs: Vec<_> = module.definitions().collect();
377
378 assert_eq!(defs.len(), 2);
379 assert_eq!(defs[0], Symbol::Object(object_id));
380 assert_eq!(defs[1], Symbol::Node(plain_node));
381 }
382
383 #[test]
384 fn definitions_keep_plain_nodes_on_type_name_collision() {
385 let mut module = ModuleData::new("TEST-MIB".to_string());
386
387 let type_id = TypeId::new(0);
388 let plain_node = NodeId::new(11);
389
390 module.add_type("DisplayString", type_id);
391 module.add_node("DisplayString", plain_node);
392
393 let defs: Vec<_> = module.definitions().collect();
394
395 assert_eq!(defs.len(), 2);
396 assert_eq!(defs[0], Symbol::Type(type_id));
397 assert_eq!(defs[1], Symbol::Node(plain_node));
398 }
399}