1 import Base 2 import ./change_tick.bend as Tick 3 import ./trace_facts.bend as TraceFacts 4 import entity_key.bend as Key 5 import compose.bend as Q 6 import capabilities.bend as Cap 7 import world.bend as W 8 import transaction.bend as T 9 10 type Error is Data: 11 MissingEntity{entityId: Key.Key} 12 QueryMismatch{entityId: Key.Key} 13 NoEntities{} 14 MultipleEntities{count: U32} 15 BodyError{error: Q.Error} 16 type Scanned<-S: Data,-Store: Type,-R: Type,-E: Data> is Type: 17 Scanned{world: W.World,handles: List<&2,W.Handle>} 18 type Executed<-World: Type,U: Data,Out: Data> is Type: 19 Executed{world: World,result: Result<&2,&2,Error,Out>} 20 21 def handle_id(~S: Data,handle: W.Handle) -> Key.Key: 22 match handle: 23 case W.Handle{_,id}: id 24 def add(~S: Data,selected: Bool,handle: W.Handle,rest: List<&2,W.Handle>) -> List<&2,W.Handle>: 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. 29 def unselect(~S: Data,~Store: Type,~R: Type,~E: Data,observed: Q.Frame & Bool) -> W.World & Bool: 30 match observed: 31 case (Q.Frame{T.Tx{world,undo,_,_,_},_,_,_},selected): (T.rollback(~W.World,undo,world),selected) 32 def prepend(~S: Data,~Store: Type,~R: Type,~E: Data,selected: Bool,handle: W.Handle,observed: Scanned) -> Scanned: 33 match observed: 34 case Scanned{world,handles}: Scanned{world,add(~S,selected,handle,handles)} 35 def scanned_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,+cursor:Maybe<&2,Tick.Tick>,handle: W.Handle,next: W.World -> Scanned,observed: W.World & Bool) -> Scanned: 36 (world,selected) = observed 37 prepend(~S,~Store,~R,~E,selected,handle,next(world)) 38 def scan_handles(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,handles: List<&2,W.Handle>,+cursor:Maybe<&2,Tick.Tick>,world: W.World) -> Scanned: 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,~E,world),handle,None{},cursor}))) 42 def scan_reserved(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,cursor:Maybe<&2,Tick.Tick>,observed: W.World & List<&2,W.Handle>) -> Scanned: 43 (world,handles) = observed 44 scan_handles(~S,~Store,~R,~E,~Ops,~plan,handles,cursor,world) 45 def scan(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,world: W.World,cursor:Maybe<&2,Tick.Tick>) -> Scanned: 46 scan_reserved(~S,~Store,~R,~E,~Ops,~plan,cursor,W.handles(~S,~Store,~R,~E,world)) 47 def count_list(~S: Data,handles: List<&2,W.Handle>,n: U32) -> U32: 48 match handles: 49 case []: n 50 case _ <> rest: count_list(~S,rest,(n + 1 : U32)) 51 def counted(~S: Data,~Store: Type,~R: Type,~E: Data,observed: Scanned) -> W.World & U32: 52 match observed: 53 case Scanned{world,handles}: (world,count_list(~S,handles,0)) 54 def count(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,world: W.World,cursor:Maybe<&2,Tick.Tick>) -> W.World & U32: 55 counted(~S,~Store,~R,~E,scan(~S,~Store,~R,~E,~Ops,~plan,world,cursor)) 56 def executed(~World: Type,~U: Data,~Out: Data,+entityId: Key.Key,observed: Q.Executed,Out>) -> Executed: 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}}} 62 def complete(~S: Data,~Store: Type,~R: Type,~E: Data,~U: Data,~Out: Data,tx: T.Tx,E>) -> T.Tx,E> & (T.Outcome> & List<&2,Out>): 63 (tx,(T.Success{},[])) 64 def scope_finished(~S:Data,~Store:Type,~R:Type,~E:Data,~U:Data,~Out:Data,owned:Bool,result:Executed,U,Out>) -> Executed,U,Out>: 65 match result: 66 case Executed{world,result}:Executed{W.finish_run(~S,~Store,~R,~E,owned,world),result} 67 def invoke_inner(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,world: W.World,+handle: W.Handle,cursor:Maybe<&2,Tick.Tick>) -> Executed,U,Out>: 68 executed(~W.World,~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 & Out),~body,~Q.Frame,~Q.plan_caps(~S,~Store,~R,~E,~Ops,plan),args,Q.Frame{T.begin(~W.World,~E,world),handle,None{},cursor})))) 69 def invoke_scoped(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,admitted:W.World & Bool,+handle: W.Handle,cursor:Maybe<&2,Tick.Tick>) -> Executed,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)) 72 def invoke(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,world: W.World,+handle: W.Handle,cursor:Maybe<&2,Tick.Tick>) -> Executed,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 75 def single_scanned(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,cursor:Maybe<&2,Tick.Tick>,observed: Scanned) -> Executed,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)}}} 80 def single(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,world: W.World,+cursor:Maybe<&2,Tick.Tick>) -> Executed,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)) 82 def lookup_selected(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,handle: W.Handle,cursor:Maybe<&2,Tick.Tick>,observed: W.World & Bool) -> Executed,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) 86 def lookup_valid(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,+handle: W.Handle,+cursor:Maybe<&2,Tick.Tick>,observed: W.World & Bool) -> Executed,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,~E,world),handle,None{},cursor}))) 90 def get(~S: Data,~Store: Type,~R: Type,~E: Data,~Ops: Type -> Type,~plan: Q.Plan,~A: Data,~U: Data,~Out: Data,~body: @-H: Type -> @-caps: Ops(H) -> A -> H -> H & (T.Outcome & Out),args: A,world: W.World,+handle: W.Handle,cursor:Maybe<&2,Tick.Tick>) -> Executed,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))