1use bytes::Bytes;
12
13use crate::abi::{EntityId, InstanceId, TypeCode};
14use crate::state::{EntityMeta, Instance};
15
16pub struct InstanceView<'a> {
18 pub(crate) instance: &'a Instance,
19}
20
21impl<'a> InstanceView<'a> {
22 pub fn id(&self) -> InstanceId {
24 self.instance.id()
25 }
26
27 pub fn entity_count(&self) -> usize {
29 self.instance.entities_len()
30 }
31
32 pub fn component_count(&self) -> usize {
34 self.instance.components_len()
35 }
36
37 pub fn local_tick(&self) -> u64 {
39 self.instance.local_tick()
40 }
41
42 pub fn entity_meta(&self, entity: EntityId) -> Option<&'a EntityMeta> {
44 self.instance.entity_meta(entity)
45 }
46
47 pub fn component(&self, entity: EntityId, type_code: TypeCode) -> Option<&'a Bytes> {
50 self.instance.component(entity, type_code)
51 }
52
53 pub fn entities(&self) -> impl Iterator<Item = (EntityId, &'a EntityMeta)> + 'a {
56 self.instance.entities_iter()
57 }
58
59 pub fn components_by_type(
64 &self,
65 type_code: TypeCode,
66 ) -> impl Iterator<Item = (EntityId, &'a Bytes)> + 'a {
67 self.instance.components_by_type_iter(type_code)
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::abi::{CapabilityMask, EntityId, Principal, Tick, TypeCode};
75 use crate::state::traits::_sealed::Sealed;
76 use crate::state::{ActionCompute, ActionContext, ActionDeriv, InstanceConfig, Op};
77 use crate::Kernel;
78 use serde::{Deserialize, Serialize};
79
80 #[derive(Serialize, Deserialize)]
83 struct SpawnManyAction {
84 count: u64,
85 type_code: u32,
86 size: u64,
87 }
88 impl Sealed for SpawnManyAction {}
89 impl ActionDeriv for SpawnManyAction {
90 const TYPE_CODE: TypeCode = TypeCode(900);
91 const SCHEMA_VERSION: u32 = 1;
92 }
93 impl ActionCompute for SpawnManyAction {
94 fn compute(&self, _ctx: &ActionContext) -> Vec<Op> {
95 let mut ops = Vec::with_capacity((self.count * 2) as usize);
96 for n in 1..=self.count {
97 let entity = EntityId::new(n).unwrap();
98 ops.push(Op::SpawnEntity {
99 id: entity,
100 owner: Principal::System,
101 });
102 ops.push(Op::SetComponent {
103 entity,
104 type_code: TypeCode(self.type_code),
105 bytes: Bytes::from(vec![0xAB; self.size as usize]),
106 size: self.size,
107 });
108 }
109 ops
110 }
111 }
112
113 fn submit(k: &mut Kernel, inst: InstanceId, action: &SpawnManyAction) {
114 use crate::state::Action;
115 let bytes = Action::canonical_bytes(action);
116 k.submit(
117 inst,
118 Principal::System,
119 None,
120 Tick(0),
121 SpawnManyAction::TYPE_CODE,
122 bytes,
123 )
124 .expect("submit ok");
125 }
126
127 fn boot() -> (Kernel, InstanceId) {
128 let mut k = Kernel::new();
129 k.register_action::<SpawnManyAction>();
130 let inst = k.create_instance(InstanceConfig::default());
131 (k, inst)
132 }
133
134 #[test]
135 fn view_none_for_missing_instance() {
136 let k = Kernel::new();
137 let bogus = InstanceId::new(99).unwrap();
138 assert!(k.instance_view(bogus).is_none());
139 }
140
141 #[test]
142 fn view_reflects_entity_count() {
143 let (mut k, inst) = boot();
144 submit(
145 &mut k,
146 inst,
147 &SpawnManyAction {
148 count: 1,
149 type_code: 7,
150 size: 10,
151 },
152 );
153 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
154
155 let view = k.instance_view(inst).expect("view present");
156 assert_eq!(view.id(), inst);
157 assert_eq!(view.entity_count(), 1);
158 assert_eq!(view.component_count(), 1);
159 }
160
161 #[test]
162 fn view_component_bytes_match_set() {
163 let (mut k, inst) = boot();
164 submit(
165 &mut k,
166 inst,
167 &SpawnManyAction {
168 count: 1,
169 type_code: 7,
170 size: 4,
171 },
172 );
173 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
174
175 let view = k.instance_view(inst).expect("view present");
176 let comp = view
177 .component(EntityId::new(1).unwrap(), TypeCode(7))
178 .expect("component present");
179 assert_eq!(comp.as_ref(), &[0xAB, 0xAB, 0xAB, 0xAB]);
180 }
181
182 #[test]
183 fn view_entities_iter_ascending() {
184 let (mut k, inst) = boot();
185 submit(
186 &mut k,
187 inst,
188 &SpawnManyAction {
189 count: 3,
190 type_code: 7,
191 size: 1,
192 },
193 );
194 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
195
196 let view = k.instance_view(inst).expect("view present");
197 let ids: Vec<u64> = view.entities().map(|(id, _)| id.get()).collect();
198 assert_eq!(ids, vec![1, 2, 3]);
199
200 let meta = view.entity_meta(EntityId::new(2).unwrap()).expect("meta");
202 assert_eq!(meta.owner, Principal::System);
203 assert_eq!(meta.created, Tick(0));
204 }
205
206 #[test]
207 fn view_components_by_type_filter() {
208 let (mut k, inst) = boot();
210 submit(
211 &mut k,
212 inst,
213 &SpawnManyAction {
214 count: 2,
215 type_code: 7,
216 size: 1,
217 },
218 );
219 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
220 submit(
222 &mut k,
223 inst,
224 &SpawnManyAction {
225 count: 2,
226 type_code: 8,
227 size: 1,
228 },
229 );
230 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
231
232 let view = k.instance_view(inst).expect("view present");
233 assert_eq!(view.component_count(), 4);
235
236 let tc7: Vec<u64> = view
237 .components_by_type(TypeCode(7))
238 .map(|(eid, _)| eid.get())
239 .collect();
240 assert_eq!(tc7, vec![1, 2]);
241
242 let tc8: Vec<u64> = view
243 .components_by_type(TypeCode(8))
244 .map(|(eid, _)| eid.get())
245 .collect();
246 assert_eq!(tc8, vec![1, 2]);
247 }
248
249 #[test]
250 fn view_local_tick_updates_after_step() {
251 let (mut k, inst) = boot();
252 submit(
253 &mut k,
254 inst,
255 &SpawnManyAction {
256 count: 1,
257 type_code: 7,
258 size: 1,
259 },
260 );
261 let pre = k.instance_view(inst).unwrap().local_tick();
263 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
264 let post = k.instance_view(inst).unwrap().local_tick();
269 assert_eq!(pre, post);
270 }
271}