1 import Base 2 import ./change_tick.bend as Tick 3 import column.bend as Col 4 import lifecycle.bend as L 5 6 # The captured ID and actual column travel with the owner in one affine token. 7 # No attach API accepts a different column or an external target ID. 8 type Cold<-S:Data,-C:Type> is Type: 9 Cold{column:Col.Column} 10 Wrapped{inner:Cold} 11 type Cell<-S:Data,-C:Type> is Type: 12 Taken{id:U32,owner:Maybe<&1,C>,stamp:L.Stamp,dirty:Bool,cold:Cold} 13 Untaken{column:Col.Column,incoming:Maybe<&1,C>,error:Unit} 14 type Projected<-S:Data,-C:Type,-V:Data> is Type: 15 Projected{cell:Cell,value:Maybe<&2,V>} 16 Deferred{cell:Cell} 17 18 def cold_column(~S:Data,~C:Type,cold:Cold) -> Col.Column: 19 match cold: 20 case Cold{column}: column 21 case Wrapped{inner}: cold_column(~S,~C,inner) 22 def detached(~S:Data,~C:Type,+id:U32,stamp:L.Stamp,result:Col.SwapOutcome) -> Cell: 23 match result: 24 case Col.Accepted{column,owner}: Taken{id,owner,stamp,False{},Cold{column}} 25 case Col.Rejected{column,incoming}: Untaken{column,incoming,Unit{}} 26 def detach_stamped(~S:Data,~C:Type,+id:U32,result:Col.Column & L.Stamp) -> Cell: 27 match result: 28 case (column,stamp): detached(~S,~C,id,stamp,Col.swap(~S,~C,column,id,None{})) 29 def detach(~S:Data,~C:Type,column:Col.Column,+id:U32) -> Cell: 30 detach_stamped(~S,~C,id,Col.lifecycle(~S,~C,column,id)) 31 32 def projected_owner(~S:Data,~C:Type,~V:Data,+id:U32,stamp:L.Stamp,dirty:Bool,cold:Cold,result:C & V) -> Projected: 33 match result: 34 case (owner,value): Projected{Taken{id,Some{owner},stamp,dirty,cold},Some{value}} 35 def project(~S:Data,~C:Type,~V:Data,~project:C -> C & V,cell:Cell) -> Projected: 36 match cell: 37 case Taken{id,None{},stamp,dirty,cold}: Projected{Taken{id,None{},stamp,dirty,cold},None{}} 38 case Taken{id,Some{owner},stamp,dirty,cold}: projected_owner(~S,~C,~V,id,stamp,dirty,cold,project(owner)) 39 case Untaken{column,incoming,error}: Deferred{Untaken{column,incoming,error}} 40 41 # Even a malformed token's attachment refusal returns its actual incoming owner. 42 # Trusted row adapters must handle this outcome before exposing a World. 43 def attach_marked(~S:Data,~C:Type,+id:U32,stamp:L.Stamp,dirty:Bool,result:Col.SwapOutcome) -> Col.SwapOutcome: 44 match dirty result: 45 case False{} result: result 46 case True{} Col.Accepted{column,previous}: Col.Accepted{Col.restore_stamp(~S,~C,column,id,stamp),previous} 47 case True{} Col.Rejected{column,incoming}: Col.Rejected{column,incoming} 48 def attach(~S:Data,~C:Type,cell:Cell) -> Col.SwapOutcome: 49 match cell: 50 case Untaken{column,incoming,_}: Col.Rejected{column,incoming} 51 case Taken{+id,owner,stamp,dirty,cold}: attach_marked(~S,~C,id,stamp,dirty,Col.swap(~S,~C,cold_column(~S,~C,cold),id,owner)) 52 53 type Replacement<-S:Data,-C:Type> is Type: 54 Replaced{cell:Cell,previous:Maybe<&1,C>,stamp:L.Stamp} 55 Refused{cell:Cell,incoming:C} 56 def replacement_present(~S:Data,~C:Type,+id:U32,+stamp:L.Stamp,cold:Cold,incoming:C,+tick:Tick.Tick,observed:Maybe<&1,C> & Bool) -> Replacement: 57 match observed: 58 case (previous,present): Replaced{Taken{id,Some{incoming},L.after_write(True{},present,stamp,tick),True{},cold},previous,stamp} 59 def owner_present(~C:Type,owner:Maybe<&1,C>) -> Maybe<&1,C> & Bool: 60 match owner: 61 case None{}: (None{},False{}) 62 case Some{owner}: (Some{owner},True{}) 63 # Caller validates entity/clock first and journals returned previous+stamp now. 64 def replace(~S:Data,~C:Type,cell:Cell,incoming:C,+tick:Tick.Tick) -> Replacement: 65 match cell: 66 case Taken{+id,owner,+stamp,_,cold}: replacement_present(~S,~C,id,stamp,cold,incoming,tick,owner_present(~C,owner)) 67 case Untaken{column,held,error}: Refused{Untaken{column,held,error},incoming}