query_contract.bend
bendvy-ecs-core@0.1.0.0 · 91 lines · 0 definitions
1import Base
2import bendvy-ecs-core@0.1.0.0/change_tick.bend as Tick
3import bendvy-ecs-core@0.1.0.0/trace_facts.bend as TraceFacts
4import entity_key.bend as Key
5import compose.bend as Q
6import capabilities.bend as Cap
7import world.bend as W
8import transaction.bend as T
9
10type Error<U: Data> is Data:
11 MissingEntity{entityId: Key.Key}
12 QueryMismatch{entityId: Key.Key}
13 NoEntities{}
14 MultipleEntities{count: U32}
15 BodyError{error: Q.Error<U>}
16type Scanned<-S: Data,-Store: Type,-R: Type,-E: Data> is Type:
17 Scanned{world: W.World<S,Store,R,E>,handles: List<&2,W.Handle<S>>}
18type Executed<-World: Type,U: Data,Out: Data> is Type:
19 Executed{world: World,result: Result<&2,&2,Error<U>,Out>}
20
21def handle_id(~S: Data,handle: W.Handle<S>) -> Key.Key:
22 match handle:
23 case W.Handle{_,id}: id
24def add(~S: Data,selected: Bool,handle: W.Handle<S>,rest: List<&2,W.Handle<S>>) -> List<&2,W.Handle<S>>:
25 match selected:
26 case True{}: handle <> rest
27 case False{}: rest
28# Selection is a read-only observation even if a trusted plan accidentally mutates.
29def unselect(~S: Data,~Store: Type,~R: Type,~E: Data,observed: Q.Frame<S,Store,R,E> & Bool) -> W.World<S,Store,R,E> & Bool:
30 match observed:
31 case (Q.Frame{T.Tx{world,undo,_,_,_},_,_,_},selected): (T.rollback(~W.World<S,Store,R,E>,undo,world),selected)
32def prepend(~S: Data,~Store: Type,~R: Type,~E: Data,selected: Bool,handle: W.Handle<S>,observed: Scanned<S,Store,R,E>) -> Scanned<S,Store,R,E>:
33 match observed:
34 case Scanned{world,handles}: Scanned{world,add(~S,selected,handle,handles)}
35def scanned_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,+cursor:Maybe<&2,Tick.Tick>,handle: W.Handle<S>,next: W.World<S,Store,R,E> -> Scanned<S,Store,R,E>,observed: W.World<S,Store,R,E> & Bool) -> Scanned<S,Store,R,E>:
36 (world,selected) = observed
37 prepend(~S,~Store,~R,~E,selected,handle,next(world))
38def scan_handles(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,handles: List<&2,W.Handle<S>>,+cursor:Maybe<&2,Tick.Tick>,world: W.World<S,Store,R,E>) -> Scanned<S,Store,R,E>:
39 match handles:
40 case []: Scanned{world,[]}
41 case +handle <> rest: scanned_join(~S,~Store,~R,~E,~Ops,~plan,cursor,handle,(world => scan_handles(~S,~Store,~R,~E,~Ops,~plan,rest,cursor,world)),unselect(~S,~Store,~R,~E,Q.plan_select(~S,~Store,~R,~E,~Ops,plan)(Q.Frame{T.begin(~W.World<S,Store,R,E>,~E,world),handle,None{},cursor})))
42def scan_reserved(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,cursor:Maybe<&2,Tick.Tick>,observed: W.World<S,Store,R,E> & List<&2,W.Handle<S>>) -> Scanned<S,Store,R,E>:
43 (world,handles) = observed
44 scan_handles(~S,~Store,~R,~E,~Ops,~plan,handles,cursor,world)
45def scan(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,world: W.World<S,Store,R,E>,cursor:Maybe<&2,Tick.Tick>) -> Scanned<S,Store,R,E>:
46 scan_reserved(~S,~Store,~R,~E,~Ops,~plan,cursor,W.handles(~S,~Store,~R,~E,world))
47def count_list(~S: Data,handles: List<&2,W.Handle<S>>,n: U32) -> U32:
48 match handles:
49 case []: n
50 case _ <> rest: count_list(~S,rest,(n + 1 : U32))
51def counted(~S: Data,~Store: Type,~R: Type,~E: Data,observed: Scanned<S,Store,R,E>) -> W.World<S,Store,R,E> & U32:
52 match observed:
53 case Scanned{world,handles}: (world,count_list(~S,handles,0))
54def count(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,world: W.World<S,Store,R,E>,cursor:Maybe<&2,Tick.Tick>) -> W.World<S,Store,R,E> & U32:
55 counted(~S,~Store,~R,~E,scan(~S,~Store,~R,~E,~Ops,~plan,world,cursor))
56def executed(~World: Type,~U: Data,~Out: Data,+entityId: Key.Key,observed: Q.Executed<World,Q.Error<U>,Out>) -> Executed<World,U,Out>:
57 match observed:
58 case Q.Executed{world,T.Failure{error},_}: Executed{world,Fail{BodyError{error}}}
59 case Q.Executed{world,T.Success{},value <> []}: Executed{world,Done{value}}
60 case Q.Executed{world,T.Success{},[]}: Executed{world,Fail{QueryMismatch{entityId}}}
61 case Q.Executed{world,T.Success{},_}: Executed{world,Fail{QueryMismatch{entityId}}}
62def complete(~S: Data,~Store: Type,~R: Type,~E: Data,~U: Data,~Out: Data,tx: T.Tx<W.World<S,Store,R,E>,E>) -> T.Tx<W.World<S,Store,R,E>,E> & (T.Outcome<Q.Error<U>> & List<&2,Out>):
63 (tx,(T.Success{},[]))
64def scope_finished(~S:Data,~Store:Type,~R:Type,~E:Data,~U:Data,~Out:Data,owned:Bool,result:Executed<W.World<S,Store,R,E>,U,Out>) -> Executed<W.World<S,Store,R,E>,U,Out>:
65 match result:
66 case Executed{world,result}:Executed{W.finish_run(~S,~Store,~R,~E,owned,world),result}
67def invoke_inner(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,world: W.World<S,Store,R,E>,+handle: W.Handle<S>,cursor:Maybe<&2,Tick.Tick>) -> Executed<W.World<S,Store,R,E>,U,Out>:
68 executed(~W.World<S,Store,R,E>,~U,~Out,handle_id(~S,handle),Q.finished(~S,~Store,~R,~E,~U,~Out,Q.body_returned(~S,~Store,~R,~E,~U,~Out,complete(~S,~Store,~R,~E,~U,~Out),Cap.invoke(~Ops,~A,~(T.Outcome<U> & Out),~body,~Q.Frame<S,Store,R,E>,~Q.plan_caps(~S,~Store,~R,~E,~Ops,plan),args,Q.Frame{T.begin(~W.World<S,Store,R,E>,~E,world),handle,None{},cursor}))))
69def invoke_scoped(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,admitted:W.World<S,Store,R,E> & Bool,+handle: W.Handle<S>,cursor:Maybe<&2,Tick.Tick>) -> Executed<W.World<S,Store,R,E>,U,Out>:
70 (world,owned)=admitted
71 scope_finished(~S,~Store,~R,~E,~U,~Out,owned,invoke_inner(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,world,handle,cursor))
72def invoke(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,world: W.World<S,Store,R,E>,+handle: W.Handle<S>,cursor:Maybe<&2,Tick.Tick>) -> Executed<W.World<S,Store,R,E>,U,Out>:
73 invoke_scoped(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,W.ensure_run(~S,~Store,~R,~E,world),handle,cursor)
74
75def single_scanned(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,cursor:Maybe<&2,Tick.Tick>,observed: Scanned<S,Store,R,E>) -> Executed<W.World<S,Store,R,E>,U,Out>:
76 match observed:
77 case Scanned{world,[]}: Executed{world,Fail{NoEntities{}}}
78 case Scanned{world,handle <> []}: invoke(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,world,handle,cursor)
79 case Scanned{world,+handles}: Executed{world,Fail{MultipleEntities{count_list(~S,handles,0)}}}
80def single(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,world: W.World<S,Store,R,E>,+cursor:Maybe<&2,Tick.Tick>) -> Executed<W.World<S,Store,R,E>,U,Out>:
81 single_scanned(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,cursor,scan(~S,~Store,~R,~E,~Ops,~plan,world,cursor))
82def lookup_selected(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,handle: W.Handle<S>,cursor:Maybe<&2,Tick.Tick>,observed: W.World<S,Store,R,E> & Bool) -> Executed<W.World<S,Store,R,E>,U,Out>:
83 match observed:
84 case (world,False{}): Executed{world,Fail{QueryMismatch{handle_id(~S,handle)}}}
85 case (world,True{}): invoke(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,world,handle,cursor)
86def lookup_valid(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,+handle: W.Handle<S>,+cursor:Maybe<&2,Tick.Tick>,observed: W.World<S,Store,R,E> & Bool) -> Executed<W.World<S,Store,R,E>,U,Out>:
87 match observed:
88 case (world,False{}): Executed{world,Fail{MissingEntity{handle_id(~S,handle)}}}
89 case (world,True{}): lookup_selected(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,handle,cursor,unselect(~S,~Store,~R,~E,Q.plan_select(~S,~Store,~R,~E,~Ops,plan)(Q.Frame{T.begin(~W.World<S,Store,R,E>,~E,world),handle,None{},cursor})))
90def get(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan<S,Store,R,E,Ops>,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome<U> & Out),args: A,world: W.World<S,Store,R,E>,+handle: W.Handle<S>,cursor:Maybe<&2,Tick.Tick>) -> Executed<W.World<S,Store,R,E>,U,Out>:
91 lookup_valid(~S,~Store,~R,~E,~Ops,~plan,~A,~U,~Out,~body,args,handle,cursor,W.valid(~S,~Store,~R,~E,world,handle))