1 import Base 2 import ordinary_declaration.bend as Identity 3 import persistence_declaration.bend as Decl 4 import decode_data.bend as D 5 import decode.bend as Decode 6 import bundle_construction.bend as Build 7 import capabilities.bend as Cap 8 9 # A retained nominal declaration supplies admission. The schema author provides 10 # an actual reversible constructor, not an implicit inverse for arbitrary Type. 11 type Factory<-S:Data,-I:Type,-P:Type,-identity:Identity.Declaration,-project:P -> P & D.Raw,-incoming:I -> I & D.Raw,-build:I -> D.Raw -> Build.Construction> is Data: 12 Factory{declaration:Decl.Constructed,inputCodec:D.Codec} 13 def factory(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,+declaration:Decl.Constructed) -> Factory:Factory{declaration,Decl.codec(~S,~P,~identity,~project,declaration)} 14 def factory_with_input_codec(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,declaration:Decl.Constructed,inputCodec:D.Codec) -> Factory:Factory{declaration,inputCodec} 15 def checked(~I:Type,~P:Type,~build:I -> D.Raw -> Build.Construction,receipt:D.Receipt) -> Build.Construction: 16 match receipt: 17 case D.Receipt{owner,D.Rejected{error}}:Build.Invalid{owner,error} 18 case D.Receipt{owner,D.Accepted{canonical}}:build(owner,canonical) 19 def construct(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,factory:Factory,input:I) -> Build.Construction: 20 match factory: 21 case Factory{_,inputCodec}:checked(~I,~P,~build,Decode.decode_owner(~I,~incoming,inputCodec,input)) 22 def request(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,+factory:Factory,input:I) -> Factory & Build.Construction: 23 (factory,construct(~S,~I,~P,~identity,~project,~incoming,~build,factory,input)) 24 # Constructed output or exact refused raw owner crosses only an owned grant. 25 def grant(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction) -> Cap.OwnedRequest,I,Build.Construction>: 26 Cap.OwnedRequest{factory => input => request(~S,~I,~P,~identity,~project,~incoming,~build,factory,input)} 27 def invoke(~S:Data,~I:Type,~P:Type,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,~A:Type,~Out:Type,~body:@-H:Type -> @-cap:Cap.OwnedRequest> -> A -> H -> H & Out,factory:Factory,args:A) -> Factory & Out: 28 Cap.invoke_owned(~(H => Cap.OwnedRequest>),~A,~Out,~body,~Factory,~grant(~S,~I,~P,~identity,~project,~incoming,~build),args,factory) 29 30 # Custom constructors may fail with any Data error. Keep decoder and constructor 31 # errors distinct rather than coercing a host/business error into a codec path. 32 type Error is Data:Validation{error:D.Error} Constructor{error:Custom} 33 type FallibleFactory<-S:Data,-I:Type,-P:Type,-Custom:Data,-identity:Identity.Declaration,-project:P -> P & D.Raw,-incoming:I -> I & D.Raw,-build:I -> D.Raw -> Build.Construction> is Data: 34 FallibleFactory{declaration:Decl.Constructed,inputCodec:D.Codec} 35 def fallible_factory(~S:Data,~I:Type,~P:Type,~Custom:Data,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,+declaration:Decl.Constructed) -> FallibleFactory:FallibleFactory{declaration,Decl.codec(~S,~P,~identity,~project,declaration)} 36 def fallible_factory_with_input_codec(~S:Data,~I:Type,~P:Type,~Custom:Data,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,declaration:Decl.Constructed,inputCodec:D.Codec) -> FallibleFactory:FallibleFactory{declaration,inputCodec} 37 def custom_result(~I:Type,~P:Type,~Custom:Data,result:Build.Construction) -> Build.Construction>: 38 match result: 39 case Build.Invalid{owner,error}:Build.Invalid{owner,Constructor{error}} 40 case Build.Constructed{owner,undo}:Build.Constructed{owner,undo} 41 def fallible_checked(~I:Type,~P:Type,~Custom:Data,~build:I -> D.Raw -> Build.Construction,receipt:D.Receipt) -> Build.Construction>: 42 match receipt: 43 case D.Receipt{owner,D.Rejected{error}}:Build.Invalid{owner,Validation{error}} 44 case D.Receipt{owner,D.Accepted{canonical}}:custom_result(~I,~P,~Custom,build(owner,canonical)) 45 def fallible_construct(~S:Data,~I:Type,~P:Type,~Custom:Data,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,factory:FallibleFactory,input:I) -> Build.Construction>: 46 match factory: 47 case FallibleFactory{_,inputCodec}:fallible_checked(~I,~P,~Custom,~build,Decode.decode_owner(~I,~incoming,inputCodec,input)) 48 def fallible_request(~S:Data,~I:Type,~P:Type,~Custom:Data,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction,+factory:FallibleFactory,input:I) -> FallibleFactory & Build.Construction>: 49 (factory,fallible_construct(~S,~I,~P,~Custom,~identity,~project,~incoming,~build,factory,input)) 50 def fallible_grant(~S:Data,~I:Type,~P:Type,~Custom:Data,~identity:Identity.Declaration,~project:P -> P & D.Raw,~incoming:I -> I & D.Raw,~build:I -> D.Raw -> Build.Construction) -> Cap.OwnedRequest,I,Build.Construction>>: 51 Cap.OwnedRequest{factory => input => fallible_request(~S,~I,~P,~Custom,~identity,~project,~incoming,~build,factory,input)}