1use std::collections::HashMap;
4
5use crate::bundle::Bundle;
6use crate::object::StixObject;
7use crate::view::ObjectView;
8
9#[derive(Debug, Clone, Default)]
12pub struct ObjectStore {
13 by_id: HashMap<String, StixObject>,
14}
15
16impl ObjectStore {
17 pub fn from_objects(objects: &[StixObject]) -> Self {
19 let mut by_id = HashMap::new();
20 for obj in objects {
21 if let Some(id) = obj.id() {
22 by_id.insert(id.to_string(), obj.clone());
23 }
24 }
25 ObjectStore { by_id }
26 }
27
28 pub fn from_bundle(bundle: &Bundle) -> Self {
30 ObjectStore::from_objects(&bundle.objects)
31 }
32
33 pub fn get(&self, id: &str) -> Option<&StixObject> {
35 self.by_id.get(id)
36 }
37
38 pub fn len(&self) -> usize {
40 self.by_id.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.by_id.is_empty()
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use crate::bundle::Bundle;
53 use crate::view::ObjectView;
54
55 fn store() -> ObjectStore {
56 let b = Bundle::from_json_str(
57 r#"{
58 "type": "bundle",
59 "id": "bundle--1",
60 "objects": [
61 {"type": "ipv4-addr", "id": "ipv4-addr--1", "value": "1.2.3.4"},
62 {"type": "domain-name", "id": "domain-name--1", "value": "evil.example"}
63 ]
64 }"#,
65 )
66 .unwrap();
67 ObjectStore::from_bundle(&b)
68 }
69
70 #[test]
71 fn resolves_by_id() {
72 let s = store();
73 let o = s.get("ipv4-addr--1").expect("should be present");
74 assert_eq!(o.property("value").unwrap().as_str(), Some("1.2.3.4"));
75 }
76
77 #[test]
78 fn missing_id_returns_none() {
79 let s = store();
80 assert!(s.get("nope--1").is_none());
81 }
82
83 #[test]
84 fn len_counts_objects() {
85 let s = store();
86 assert_eq!(s.len(), 2);
87 assert!(!s.is_empty());
88 }
89}