1 import Base 2 import ./execution_save_plan.bend as Plan 3 import ./execution_save_traversal.bend as Traverse 4 import ./execution_save_rows.bend as Rows 5 import ./execution_save_data.bend as Saved 6 import ./execution_save_recipe.bend as Recipe 7 import ./execution_save_metadata.bend as Metadata 8 import ./execution_save_gate.bend as Gate 9 import ./execution_composer_layout.bend as Layout 10 import ./snapshot_data.bend as Legacy 11 import ./world.bend as W 12 import ./entity_allocator.bend as Alloc 13 import ./clock_state.bend as Clock 14 import ./execution_lifecycle_notices.bend as Notice 15 import ./deferred_command.bend as Deferred 16 17 type Remainder<-S:Data,-C:Type,-R:Type,-E:Data> is Type: 18 Remainder{namespace:U32,allocator:Alloc.Allocator,events:List<&2,E>,pending:List>>,registrations:List<&2,W.RegistrationMeta>,nextSystemId:U32,clock:Clock.State,journal:Notice.Journal} 19 def assembled(~S:Data,~C:Type,~R:Type,~E:Data,rest:Remainder,columns:C,resources:R) -> W.World: 20 match rest: 21 case Remainder{namespace,allocator,events,pending,registrations,nextSystemId,clock,journal}:W.World{namespace,allocator,columns,resources,events,pending,registrations,nextSystemId,clock,journal} 22 def exported(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,componentRecipes:Plan.Recipes(~S,cp),columns:Layout.columns(~S,Plan.layout(cp)),rest:Remainder,metadata:Metadata.Metadata,rows:List<&2,Saved.Entity>,result:Plan.Recipes(~S,rp) & (Layout.resources(~S,Plan.layout(rp)) & Saved.Fields)) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 23 match result: 24 case (resourceRecipes,(resources,Saved.Fields{_,Some{error}})):(Plan.Schema{componentRecipes,resourceRecipes},(assembled(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,rest,columns,resources),Gate.Refused{error})) 25 case (resourceRecipes,(resources,Saved.Fields{fields,None{}})):(Plan.Schema{componentRecipes,resourceRecipes},(assembled(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,rest,columns,resources),Gate.Saved{Saved.Snapshot{2,metadata,rows,fields}})) 26 def rows_returned(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,resourceRecipes:Plan.Recipes(~S,rp),resources:Layout.resources(~S,Plan.layout(rp)),rest:Remainder,metadata:Metadata.Metadata,result:Plan.Recipes(~S,cp) & (Layout.columns(~S,Plan.layout(cp)) & (List<&2,Saved.Entity> & Maybe<&2,Recipe.Refusal>))) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 27 match result: 28 case (componentRecipes,(columns,(_,Some{error}))):(Plan.Schema{componentRecipes,resourceRecipes},(assembled(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,rest,columns,resources),Gate.Refused{error})) 29 case (componentRecipes,(columns,(rows,None{}))):exported(~S,~cp,~rp,~E,componentRecipes,columns,rest,metadata,rows,Traverse.resources(~S,rp)(resourceRecipes,resources)) 30 def handles_returned(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,schema:Plan.Schema,metadata:Metadata.Metadata,result:W.World & List<&2,W.Handle>) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 31 match schema result: 32 case Plan.Schema{componentRecipes,resourceRecipes}, (W.World{namespace,allocator,columns,resources,events,pending,registrations,nextSystemId,clock,journal},handles):rows_returned(~S,~cp,~rp,~E,resourceRecipes,resources,Remainder{namespace,allocator,events,pending,registrations,nextSystemId,clock,journal},metadata,Rows.rows(~S,~cp,handles,componentRecipes,columns)) 33 def metadata_returned(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,schema:Plan.Schema,result:W.World & Metadata.Metadata) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 34 match result: 35 case (world,metadata):handles_returned(~S,~cp,~rp,~E,schema,metadata,W.handles(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,world)) 36 def unique(valid:Bool) -> Maybe<&2,Recipe.Refusal>: 37 match valid: 38 case True{}:None{} 39 case False{}:Some{Recipe.DescriptorMismatch{"unique layout keys","duplicate key"}} 40 def eligibility_result(result:Maybe<&2,String>,valid:Bool) -> Maybe<&2,Recipe.Refusal>: 41 match result: 42 case Some{key}:Some{Recipe.PlainUnserializable{key}} 43 case None{}:unique(valid) 44 def eligibility(~cp:Plan.Plan,~rp:Plan.Plan) -> Maybe<&2,Recipe.Refusal>: 45 eligibility_result(Plan.first(Plan.ineligible(cp),Plan.ineligible(rp)),Bool.and(Layout.unique(Plan.layout(cp)),Layout.unique(Plan.layout(rp)))) 46 def eligible(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,schema:Plan.Schema,world:W.World,error:Maybe<&2,Recipe.Refusal>) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 47 match error: 48 case Some{error}:(schema,(world,Gate.Refused{error})) 49 case None{}:metadata_returned(~S,~cp,~rp,~E,schema,Metadata.capture(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,world)) 50 def ready(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,schema:Plan.Schema,result:W.World & Bool) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 51 match result: 52 case (world,False{}):(schema,(world,Gate.RefusedPending{})) 53 case (world,True{}):eligible(~S,~cp,~rp,~E,schema,world,eligibility(~cp,~rp)) 54 # Internal World phase. Ordinary applications must first inventory their actual 55 # command owners with execution-save-gate; legacy World.pending is additional. 56 # Eligibility of the entire closed schema precedes metadata and field projection. 57 def save(~S:Data,~cp:Plan.Plan,~rp:Plan.Plan,~E:Data,schema:Plan.Schema,world:W.World) -> Plan.Schema & (W.World & Gate.Outcome,Recipe.Refusal>): 58 ready(~S,~cp,~rp,~E,schema,Legacy.ready(~S,~Layout.columns(~S,Plan.layout(cp)),~Layout.resources(~S,Plan.layout(rp)),~E,world))