removals.bend
bendvy-ecs-core@0.1.0.1 · 122 lines · 0 definitions
1import Base
2import bendvy-ecs-core@0.1.0.1/world.bend as W
3import bendvy-ecs-core@0.1.0.1/system.bend as Sys
4import bendvy-ecs-core@0.1.0.1/registration.bend as Registration
5
6# Removal streams are retained metadata, not component owners or ordinary events.
7# Schema/family are phantom indices; each closed selector defines its typed stream.
8type Record<S: Data,F: Data> is Data:
9 Removed{entity: W.Handle<S>,family: F}
10 Despawned{entity: W.Handle<S>}
11type Reader<S: Data,E: Data,F: Data,-select: E -> Maybe<&2,Record<S,F>>> is Type:
12 Reader{namespace: U32,id: U32,name: String,cursor: Nat}
13type Registered<S: Data,C: Type,R: Type,E: Data,F: Data,-select: E -> Maybe<&2,Record<S,F>>> is Type:
14 Registered{world: W.World<S,C,R,E>,reader: Reader<S,E,F,select>}
15 Rejected{world: W.World<S,C,R,E>}
16type Run<S: Data,C: Type,R: Type,E: Data,F: Data,-select: E -> Maybe<&2,Record<S,F>>,Error: Data,Out: Type> is Type:
17 Completed{reader: Reader<S,E,F,select>,outcome: Sys.Outcome<W.World<S,C,R,E>,Error,Out>}
18 Invalid{reader: Reader<S,E,F,select>,world: W.World<S,C,R,E>}
19
20type Disposal<S: Data,C: Type,R: Type,E: Data,F: Data,-select: E -> Maybe<&2,Record<S,F>>> is Type:
21 Disposed{world: W.World<S,C,R,E>}
22 DisposalRejected{world: W.World<S,C,R,E>,reader: Reader<S,E,F,select>}
23
24def count(~E: Data,xs: List<&2,E>) -> Nat:
25 match xs:
26 case []: 0n
27 case _ <> rest: 1n+count(~E,rest)
28
29def include(~S: Data,~F: Data,item: Maybe<&2,Record<S,F>>,rest: List<&2,Record<S,F>>) -> List<&2,Record<S,F>>:
30 match item:
31 case None{}: rest
32 case Some{value}: value <> rest
33
34def since(~S: Data,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,xs: List<&2,E>,cursor: Nat) -> List<&2,Record<S,F>>:
35 match xs cursor:
36 case [] _: []
37 case _ <> rest 1n+p: since(~S,~E,~F,~select,rest,p)
38 case value <> rest 0n: include(~S,~F,select(value),since(~S,~E,~F,~select,rest,0n))
39
40def register_result(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,registered: W.World<S,C,R,E> & W.Registration,+namespace: U32,+name: String,cursor: Nat) -> Registered<S,C,R,E,F,select>:
41 match registered:
42 case (world,W.Registered{id}): Registered{world,Reader{namespace,id,name,cursor}}
43 case (world,W.RegistrationRejected{_}): Rejected{world}
44
45def register_view(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,viewed: W.World<S,C,R,E> & List<&2,E>,+namespace: U32,+name: String) -> Registered<S,C,R,E,F,select>:
46 (world,values) = viewed
47 register_result(~S,~C,~R,~E,~F,~select,W.register(~S,~C,~R,~E,world,name,["removal",name]),namespace,name,0n)
48
49def register_named(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,named: W.World<S,C,R,E> & U32,+name: String) -> Registered<S,C,R,E,F,select>:
50 (world,+namespace) = named
51 register_view(~S,~C,~R,~E,~F,~select,W.events_view(~S,~C,~R,~E,world),namespace,name)
52
53def register(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,world: W.World<S,C,R,E>,name: String) -> Registered<S,C,R,E,F,select>:
54 register_named(~S,~C,~R,~E,~F,~select,W.namespace(~S,~C,~R,~E,world),name)
55
56def finished(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,reader: Reader<S,E,F,select>,next: Nat,outcome: Sys.Outcome<W.World<S,C,R,E>,Error,Out>) -> Run<S,C,R,E,F,select,Error,Out>:
57 match reader outcome:
58 case reader Sys.Failed{world,error}: Completed{reader,Sys.Failed{world,error}}
59 case Reader{namespace,id,name,_} Sys.Succeeded{world,out}: Completed{Reader{namespace,id,name,next},Sys.Succeeded{world,out}}
60
61def consumed(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,reader: Reader<S,E,F,select>,next: Nat,world: W.World<S,C,R,E>,result: Result<&2,&1,Error,Out>) -> Run<S,C,R,E,F,select,Error,Out>:
62 match result:
63 case Done{out}: finished(~S,~C,~R,~E,~F,~select,~Error,~Out,reader,next,Sys.Succeeded{world,out})
64 case Fail{error}: finished(~S,~C,~R,~E,~F,~select,~Error,~Out,reader,next,Sys.Failed{world,error})
65
66def viewed(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,~consume: List<&2,Record<S,F>> -> Result<&2,&1,Error,Out>,reader: Reader<S,E,F,select>,cursor: Nat,viewed: W.World<S,C,R,E> & List<&2,E>) -> Run<S,C,R,E,F,select,Error,Out>:
67 (world,+values) = viewed
68 result = consume(since(~S,~E,~F,~select,values,cursor))
69 consumed(~S,~C,~R,~E,~F,~select,~Error,~Out,reader,count(~E,values),world,result)
70
71def checked(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,~consume: List<&2,Record<S,F>> -> Result<&2,&1,Error,Out>,allowed: Bool,reader: Reader<S,E,F,select>,world: W.World<S,C,R,E>,cursor: Nat) -> Run<S,C,R,E,F,select,Error,Out>:
72 match allowed:
73 case False{}: Invalid{reader,world}
74 case True{}: viewed(~S,~C,~R,~E,~F,~select,~Error,~Out,~consume,reader,cursor,W.events_view(~S,~C,~R,~E,world))
75
76def named(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,~consume: List<&2,Record<S,F>> -> Result<&2,&1,Error,Out>,reader: Reader<S,E,F,select>,world: W.World<S,C,R,E>,actual: U32) -> Run<S,C,R,E,F,select,Error,Out>:
77 match reader world:
78 case Reader{+namespace,+id,+name,+cursor} W.World{+ns,allocator,store,resource,events,pending,+registrations,nextSystemId,clock}:
79 checked(~S,~C,~R,~E,~F,~select,~Error,~Out,~consume,Bool.and(U32.is_eq(namespace,actual),W.registration_list_matches(registrations,id,name,["removal",name])),Reader{namespace,id,name,cursor},W.World{ns,allocator,store,resource,events,pending,registrations,nextSystemId,clock},cursor)
80
81def run(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,~Error: Data,~Out: Type,~consume: List<&2,Record<S,F>> -> Result<&2,&1,Error,Out>,reader: Reader<S,E,F,select>,world: W.World<S,C,R,E>) -> Run<S,C,R,E,F,select,Error,Out>:
82 match world:
83 case W.World{+ns,allocator,store,resource,events,pending,registrations,nextSystemId,clock}: named(~S,~C,~R,~E,~F,~select,~Error,~Out,~consume,reader,W.World{ns,allocator,store,resource,events,pending,registrations,nextSystemId,clock},ns)
84
85# Skipped removal readers keep their original cursor; unlike ordinary events.
86def skip(~S: Data,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,reader: Reader<S,E,F,select>) -> Reader<S,E,F,select>:
87 reader
88
89def removal_access(access: List<&2,String>) -> Bool:
90 match access:
91 case []: False{}
92 case first <> _: String.eq(first,"removal")
93
94def readers_remain(entries: List<&2,W.RegistrationMeta>) -> Bool:
95 match entries:
96 case []: False{}
97 case W.RegistrationMeta{_,_,access} <> rest: Bool.or(removal_access(access),readers_remain(rest))
98
99def collect_checked(~S: Data,~C: Type,~R: Type,~E: Data,retain: Bool,world: W.World<S,C,R,E>) -> W.World<S,C,R,E>:
100 match retain world:
101 case True{} world: world
102 case False{} W.World{namespace,allocator,store,resource,_,pending,registrations,nextSystemId,clock}: W.World{namespace,allocator,store,resource,[],pending,registrations,nextSystemId,clock}
103
104def collected(~S: Data,~C: Type,~R: Type,~E: Data,result: W.World<S,C,R,E> & Bool) -> W.World<S,C,R,E> & Bool:
105 match result:
106 case (world,False{}): (world,False{})
107 case (W.World{namespace,allocator,store,resource,events,pending,+registrations,nextSystemId,clock},True{}): (collect_checked(~S,~C,~R,~E,readers_remain(registrations),W.World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock}),True{})
108
109def disposal_collected(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,result: W.World<S,C,R,E> & Bool) -> Disposal<S,C,R,E,F,select>:
110 (world,removed) = result
111 Disposed{world}
112
113def disposal_result(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,reader: Reader<S,E,F,select>,result: W.World<S,C,R,E> & Bool) -> Disposal<S,C,R,E,F,select>:
114 match result:
115 case (world,False{}): DisposalRejected{world,reader}
116 case (world,True{}): disposal_collected(~S,~C,~R,~E,~F,~select,collected(~S,~C,~R,~E,(world,True{})))
117
118# This owner is removal-only: ordinary event retention uses its own Runtime.
119# Disposing the last registered reader releases the retained metadata log.
120def dispose(~S: Data,~C: Type,~R: Type,~E: Data,~F: Data,~select: E -> Maybe<&2,Record<S,F>>,reader: Reader<S,E,F,select>,world: W.World<S,C,R,E>) -> Disposal<S,C,R,E,F,select>:
121 match reader:
122 case Reader{+namespace,+id,+name,+cursor}: disposal_result(~S,~C,~R,~E,~F,~select,Reader{namespace,id,name,cursor},Registration.remove(~S,~C,~R,~E,world,namespace,id,name,["removal",name]))