1 import Base 2 import ./world.bend as W 3 import ./ordinary_schema.bend as Schema 4 import ./ordinary_app_schema.bend as SchemaApp 5 import ./ordinary_app_registration_fields.bend as Records 6 import ./ordinary_query_declaration.bend as Query 7 import ./ordinary_app_composer.bend as App 8 import ./ordinary_app_recipe.bend as Tree 9 import ./ordinary_app_observation_data.bend as DTO 10 import ./ordinary_app_description.bend as Description 11 12 type Category is Data: 13 Components{} 14 Resources{} 15 Events{} 16 Services{} 17 type Role is Data: 18 Reader{} 19 Writer{} 20 Filter{} 21 type Use is Data: 22 Use{namespace:U32,id:U32,name:String,mode:Query.Mode,role:Role} 23 type Entry is Data: 24 Entry{category:Category,key:String,uses:List<&2,Use>} 25 # Typed modes retain the exact operational declaration, including filter-only modes. 26 def resource_fields(~S:Data,+mode:Query.Mode,access:List<&2,String>) -> List<&2,Query.Clause>: 27 match access: 28 case []:[] 29 case name <> rest:Query.Clause{name,mode} <> resource_fields(~S,mode,rest) 30 def clauses(~S:Data,kind:Records.RegistrationKind,access:List<&2,String>,retained:List<&2,Query.Clause>) -> List<&2,Query.Clause>: 31 match kind: 32 case Records.ComponentKind{}:retained 33 case Records.FieldKind{}:retained 34 case Records.ServiceKind{}:[] 35 case Records.EventKind{}:retained 36 case Records.MixedKind{uses}:Description.mixed_clauses(~S,uses) 37 case Records.ResourceKind{mode}:resource_fields(~S,mode,access) 38 def role(mode:Query.Mode) -> Role: 39 match mode: 40 case Query.Write{}:Writer{} 41 case Query.With{}:Filter{} 42 case Query.Without{}:Filter{} 43 case Query.Read{}:Reader{} 44 case Query.Optional{}:Reader{} 45 case Query.Added{}:Reader{} 46 case Query.Changed{}:Reader{} 47 def entries(~S:Data,+category:Category,+namespace:U32,+id:U32,+name:String,clauses:List<&2,Query.Clause>) -> List<&2,Entry>: 48 match clauses: 49 case []:[] 50 case Query.Clause{key,+mode} <> rest:Entry{category,key,[Use{namespace,id,name,mode,role(mode)}]} <> entries(~S,category,namespace,id,name,rest) 51 def mixed_category(category:Records.UseCategory) -> Category: 52 match category: 53 case Records.ComponentUse{}:Components{} 54 case Records.ResourceUse{}:Resources{} 55 case Records.EventUse{}:Events{} 56 def mixed_entries(~S:Data,+namespace:U32,+id:U32,+name:String,uses:List<&2,Records.DeclaredUse>) -> List<&2,Entry>: 57 match uses: 58 case []:[] 59 case Records.DeclaredUse{category,_,key,+mode} <> rest:Entry{mixed_category(category),key,[Use{namespace,id,name,mode,role(mode)}]} <> mixed_entries(~S,namespace,id,name,rest) 60 def kind_entries(~S:Data,+namespace:U32,+id:U32,+name:String,slot:String,access:List<&2,String>,retained:List<&2,Query.Clause>,kind:Records.RegistrationKind) -> List<&2,Entry>: 61 match kind: 62 case Records.ComponentKind{}:entries(~S,Components{},namespace,id,name,retained) 63 case Records.FieldKind{}:entries(~S,Resources{},namespace,id,name,retained) 64 case Records.ServiceKind{}:[Entry{Services{},slot,[]}] 65 case Records.EventKind{}:entries(~S,Events{},namespace,id,name,retained) 66 case Records.ResourceKind{mode}:entries(~S,Resources{},namespace,id,name,resource_fields(~S,mode,access)) 67 case Records.MixedKind{uses}:mixed_entries(~S,namespace,id,name,uses) 68 def fields(~S:Data,records:List<&2,Records.Fields>) -> List<&2,Entry>: 69 match records: 70 case []:[] 71 case Records.Fields{namespace,id,name,access,_,slot,retained,kind} <> rest:List.append(&2,Entry,kind_entries(~S,namespace,id,name,slot,access,retained,kind),fields(~S,rest)) 72 def same_category(left:Category,right:Category) -> Bool: 73 match left right: 74 case Components{} Components{}:True{} 75 case Resources{} Resources{}:True{} 76 case Events{} Events{}:True{} 77 case Services{} Services{}:True{} 78 case _ _:False{} 79 def selected(found:Bool,category:Category,key:String,uses:List<&2,Use>,other:Category,otherKey:String,otherUses:List<&2,Use>,rest:List<&2,Entry>,tail:Unit -> List<&2,Entry>) -> List<&2,Entry>: 80 match found: 81 case True{}:Entry{other,otherKey,List.append(&2,Use,otherUses,uses)} <> rest 82 case False{}:Entry{other,otherKey,otherUses} <> tail(Unit{}) 83 def insert(+category:Category,+key:String,+uses:List<&2,Use>,entries:List<&2,Entry>) -> List<&2,Entry>: 84 match entries: 85 case []:[Entry{category,key,uses}] 86 case Entry{+other,+otherKey,+otherUses} <> +rest:selected(Bool.and(same_category(category,other),String.eq(key,otherKey)),category,key,uses,other,otherKey,otherUses,rest,_ => insert(category,key,uses,rest)) 87 def grouped(entries:List<&2,Entry>,index:List<&2,Entry>) -> List<&2,Entry>: 88 match entries: 89 case []:index 90 case Entry{category,key,uses} <> rest:grouped(rest,insert(category,key,uses,index)) 91 def folded(~S:Data,~World:Type,~Owners:Type,~Rows:Data,~observe:World -> World & DTO.Observation,~fold:Owners -> Owners & List<&2,Records.Fields>,world:World,recipe:Tree.Recipe,result:Owners & List<&2,Records.Fields>) -> App.Registered & Maybe<&2,List<&2,Entry>>: 92 match result: 93 case (owners,records):(App.Registered{world,owners,recipe,True{}},Some{grouped(fields(~S,records),[])}) 94 def index(~S:Data,~World:Type,~Owners:Type,~Rows:Data,~observe:World -> World & DTO.Observation,~fold:Owners -> Owners & List<&2,Records.Fields>,app:App.Registered) -> App.Registered & Maybe<&2,List<&2,Entry>>: 95 match app: 96 case App.Registered{world,owners,recipe,False{}}:(App.Registered{world,owners,recipe,False{}},None{}) 97 case App.Registered{world,owners,recipe,True{}}:folded(~S,~World,~Owners,~Rows,~observe,~fold,world,recipe,fold(owners)) 98 99 def schema_returned(~S:Data,~C:Type,~R:Type,~E:Data,~Owners:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:Owners -> Owners & List<&2,Records.Fields>,schema:Schema.Schema,result:App.Registered,Owners,Rows,observe,fold> & Maybe<&2,List<&2,Entry>>) -> SchemaApp.Application & Maybe<&2,List<&2,Entry>>: 100 match result: 101 case (app,view):(SchemaApp.Application{schema,app},view) 102 def schema_index(~S:Data,~C:Type,~R:Type,~E:Data,~Owners:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:Owners -> Owners & List<&2,Records.Fields>,value:SchemaApp.Application) -> SchemaApp.Application & Maybe<&2,List<&2,Entry>>: 103 match value: 104 case SchemaApp.Application{schema,app}:schema_returned(~S,~C,~R,~E,~Owners,~Rows,~observe,~fold,schema,index(~S,~W.World,~Owners,~Rows,~observe,~fold,app))