removals.bend

bendvy-ecs-core@0.1.0.0 · 122 lines · 0 definitions

1import Base
2import bendvy-ecs-core@0.1.0.0/world.bend as W
3import bendvy-ecs-core@0.1.0.0/system.bend as Sys
4import bendvy-ecs-core@0.1.0.0/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]))