1 import Base 2 import ./execution_command_permission.bend as Permission 3 import ./execution_composer_layout.bend as Layout 4 import ./execution_resource_policy.bend as Policy 5 import ./execution_relation_resource_snapshot.bend as Snapshot 6 import ./execution_relation_failure_retention.bend as Retention 7 8 # Cleanup may mutate every declared relation family. It refuses before mutation 9 # if the accepted catalogue omits any protected graph in the actual root layout. 10 def contains(~S:Data,~resources:Layout.Layout,+route:Layout.Route,relations:Permission.Relations) -> Bool: 11 match relations: 12 case Permission.NoRelations{}:False{} 13 case Permission.RelationAllowed{selected,_,_,_,_,_,rest}:Bool.or(Permission.same_route(route,selected),contains(~S,~resources,route,rest)) 14 case Permission.ScopeAllowed{_,_,_,_,_,rest}:contains(~S,~resources,route,rest) 15 def append(prefix:Layout.Route,+suffix:Layout.Route) -> Layout.Route: 16 match prefix: 17 case Layout.AtLeaf{}:suffix 18 case Layout.InLeft{rest}:Layout.InLeft{append(rest,suffix)} 19 case Layout.InRight{rest}:Layout.InRight{append(rest,suffix)} 20 def leaf(~S:Data,~resources:Layout.Layout,policy:Policy.Policy,route:Layout.Route,relations:Permission.Relations) -> Bool: 21 match policy: 22 case Policy.ProtectedRelation{_,_}:contains(~S,~resources,route,relations) 23 case Policy.Ordinary{}:True{} 24 case Policy.ProtectedLifetimeScope{_}:True{} 25 def layout(~S:Data,~resources:Layout.Layout,current:Layout.Layout,+prefix:Layout.Route,+relations:Permission.Relations) -> Bool: 26 match current: 27 case Layout.EmptyLayout{}:True{} 28 case Layout.LayoutLeaf{_,_,_,_,policy}:leaf(~S,~resources,policy,prefix,relations) 29 case Layout.ClassifiedLayoutLeaf{_,_,_,_,_,policy}:leaf(~S,~resources,policy,prefix,relations) 30 case Layout.LayoutPair{left,right}:Bool.and(layout(~S,~resources,left,append(prefix,Layout.InLeft{Layout.AtLeaf{}}),relations),layout(~S,~resources,right,append(prefix,Layout.InRight{Layout.AtLeaf{}}),relations)) 31 def namespaces(~S:Data,+namespace:U32,views:List<&2,Snapshot.GraphView>) -> Bool: 32 match views: 33 case []:True{} 34 case Snapshot.GraphView{_,_,stored,_} <> rest:Bool.and(U32.is_eq(namespace,stored),namespaces(~S,namespace,rest)) 35 36 def scope_contains(-S:Data,-resources:Layout.Layout,+route:Layout.Route,relations:Permission.Relations) -> Bool: 37 match relations: 38 case Permission.NoRelations{}:False{} 39 case Permission.RelationAllowed{_,_,_,_,_,_,rest}:scope_contains(S,resources,route,rest) 40 case Permission.ScopeAllowed{selected,_,_,_,_,rest}:Bool.or(Permission.same_route(route,selected),scope_contains(S,resources,route,rest)) 41 def scopes_leaf(-S:Data,-resources:Layout.Layout,policy:Policy.Policy,route:Layout.Route,relations:Permission.Relations) -> Bool: 42 match policy: 43 case Policy.ProtectedLifetimeScope{_}:scope_contains(S,resources,route,relations) 44 case Policy.ProtectedRelation{_,_}:True{} 45 case Policy.Ordinary{}:True{} 46 def scopes_layout(-S:Data,-resources:Layout.Layout,current:Layout.Layout,+prefix:Layout.Route,+relations:Permission.Relations) -> Bool: 47 match current: 48 case Layout.EmptyLayout{}:True{} 49 case Layout.LayoutLeaf{_,_,_,_,policy}:scopes_leaf(S,resources,policy,prefix,relations) 50 case Layout.ClassifiedLayoutLeaf{_,_,_,_,_,policy}:scopes_leaf(S,resources,policy,prefix,relations) 51 case Layout.LayoutPair{left,right}:Bool.and(scopes_layout(S,resources,left,append(prefix,Layout.InLeft{Layout.AtLeaf{}}),relations),scopes_layout(S,resources,right,append(prefix,Layout.InRight{Layout.AtLeaf{}}),relations)) 52 def scope_routes(-S:Data,-resources:Layout.Layout,relations:Permission.Relations) -> List<&2,Layout.Route>: 53 match relations: 54 case Permission.NoRelations{}:[] 55 case Permission.RelationAllowed{_,_,_,_,_,_,rest}:scope_routes(S,resources,rest) 56 case Permission.ScopeAllowed{route,_,_,_,_,rest}:route <> scope_routes(S,resources,rest) 57 def protected_leaf(policy:Policy.Policy) -> Bool: 58 match policy: 59 case Policy.ProtectedRelation{_,_}:True{} 60 case Policy.ProtectedLifetimeScope{_}:True{} 61 case Policy.Ordinary{}:False{} 62 def has_protected(current:Layout.Layout) -> Bool: 63 match current: 64 case Layout.EmptyLayout{}:False{} 65 case Layout.LayoutLeaf{_,_,_,_,policy}:protected_leaf(policy) 66 case Layout.ClassifiedLayoutLeaf{_,_,_,_,_,policy}:protected_leaf(policy) 67 case Layout.LayoutPair{left,right}:Bool.or(has_protected(left),has_protected(right)) 68 def provisioned_leaf(policy:Policy.Policy,route:Layout.Route,graphs:List<&2,Layout.Route>,scopes:List<&2,Layout.Route>) -> Bool: 69 match policy: 70 case Policy.Ordinary{}:True{} 71 case Policy.ProtectedRelation{_,_}:Retention.contains(route,graphs) 72 case Policy.ProtectedLifetimeScope{_}:Retention.contains(route,scopes) 73 def provisioned_layout(current:Layout.Layout,+prefix:Layout.Route,+graphs:List<&2,Layout.Route>,+scopes:List<&2,Layout.Route>) -> Bool: 74 match current: 75 case Layout.EmptyLayout{}:True{} 76 case Layout.LayoutLeaf{_,_,_,_,policy}:provisioned_leaf(policy,prefix,graphs,scopes) 77 case Layout.ClassifiedLayoutLeaf{_,_,_,_,_,policy}:provisioned_leaf(policy,prefix,graphs,scopes) 78 case Layout.LayoutPair{left,right}:Bool.and(provisioned_layout(left,append(prefix,Layout.InLeft{Layout.AtLeaf{}}),graphs,scopes),provisioned_layout(right,append(prefix,Layout.InRight{Layout.AtLeaf{}}),graphs,scopes)) 79 def cleanup_complete(~S:Data,~resources:Layout.Layout,+relations:Permission.Relations,routes:Maybe<&2,Permission.CleanupRoutes>) -> Bool: 80 match routes: 81 case None{}:Bool.and(layout(~S,~resources,resources,Layout.AtLeaf{},relations),scopes_layout(S,resources,resources,Layout.AtLeaf{},relations)) 82 case Some{Permission.CleanupRoutes{graphs,scopes}}:provisioned_layout(resources,Layout.AtLeaf{},graphs,scopes) 83 def cleanup_scopes(-S:Data,-resources:Layout.Layout,relations:Permission.Relations,routes:Maybe<&2,Permission.CleanupRoutes>) -> List<&2,Layout.Route>: 84 match routes: 85 case None{}:scope_routes(S,resources,relations) 86 case Some{Permission.CleanupRoutes{_,scopes}}:scopes