1 import Base 2 import entity_key.bend as K 3 import relation_types.bend as T 4 import relation_graph.bend as C 5 6 # Maintained inverse reorder: preserve authoritative targets; replace selected inverse order. 7 def child_ids(children: List<&2,T.ChildStatus>) -> List<&2,K.Key>: 8 match children: 9 case []: [] 10 case T.ChildStatus{id,_} <> rest: id <> child_ids(rest) 11 def parent_match(value: Maybe<&2,K.Key>,parent: K.Key) -> Bool: 12 match value: 13 case None{}: False{} 14 case Some{id}: K.equal(id,parent) 15 def same_count(a: List<&2,K.Key>,b: List<&2,K.Key>) -> Bool: 16 match a b: 17 case Nil{} Nil{}: True{} 18 case Con{_,ar} Con{_,br}: same_count(ar,br) 19 case _ _: False{} 20 def subset(a: List<&2,K.Key>,+b: List<&2,K.Key>) -> Bool: 21 match a: 22 case []: True{} 23 case x <> rest: Bool.and(C.contains(b,x),subset(rest,b)) 24 def child_error(live: Bool,duplicate: Bool,related: Bool,parent: K.Key,child: K.Key,+name: String) -> Maybe<&2,T.Error>: 25 match live duplicate related: 26 case False{} _ _: Some{T.MissingChildEntity{parent,child,name}} 27 case True{} True{} _: Some{T.DuplicateChild{parent,child,name}} 28 case True{} False{} False{}: Some{T.ChildNotRelatedToParent{parent,child,name}} 29 case True{} False{} True{}: None{} 30 def first_error(current: Maybe<&2,T.Error>,next: Unit -> Maybe<&2,T.Error>) -> Maybe<&2,T.Error>: 31 match current: 32 case Some{e}: Some{e} 33 case None{}: next(Unit{}) 34 def children_checked_erased(-S: Data,children: List<&2,T.ChildStatus>,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+seen: List<&2,K.Key>) -> Maybe<&2,T.Error>: 35 match children: 36 case []: None{} 37 case T.ChildStatus{+id,live} <> rest: first_error(child_error(live,C.contains(seen,id),parent_match(C.target_erased(S,graph,d,id),parent),parent,id,C.name_erased(S,d)),_ => children_checked_erased(S,rest,graph,d,parent,id <> seen)) 38 def mismatch(same: Bool,error: Maybe<&2,T.Error>,parent: K.Key,name: String) -> Maybe<&2,T.Error>: 39 match same error: 40 case _ Some{e}: Some{e} 41 case True{} None{}: None{} 42 case False{} None{}: Some{T.ChildSetMismatch{parent,name}} 43 def validate_erased(-S: Data,parentLive: Bool,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+children: List<&2,T.ChildStatus>) -> Maybe<&2,T.Error>: 44 match parentLive: 45 case False{}: Some{T.MissingEntity{parent}} 46 case True{}: mismatch(Bool.and(same_count(C.inverse_erased(S,graph,d,parent),child_ids(children)),subset(C.inverse_erased(S,graph,d,parent),child_ids(children))),children_checked_erased(S,children,graph,d,parent,[]),parent,C.name_erased(S,d)) 47 # Maintained inverse reorder changes only the selected inverse entry. 48 def replaced_erased(-S: Data,found: Bool,entry: T.Inverse,sources: List<&2,K.Key>,rest: List<&2,T.Inverse>) -> List<&2,T.Inverse>: 49 match found entry: 50 case True{} T.Inverse{d,parent,_}: T.Inverse{d,parent,sources} <> rest 51 case False{} entry: entry <> rest 52 def replace_inverse_erased(-S: Data,entries: List<&2,T.Inverse>,+d: T.Descriptor,+parent: K.Key,+sources: List<&2,K.Key>) -> List<&2,T.Inverse>: 53 match entries: 54 case []: [] 55 case T.Inverse{+other,+id,old} <> rest: replaced_erased(S,Bool.and(U32.is_eq(C.key_erased(S,d),C.key_erased(S,other)),K.equal(parent,id)),T.Inverse{other,id,old},sources,replace_inverse_erased(S,rest,d,parent,sources)) 56 def checked_erased(-S: Data,error: Maybe<&2,T.Error>,graph: T.Graph,d: T.Descriptor,parent: K.Key,children: List<&2,T.ChildStatus>) -> T.CoreResult: 57 match error graph: 58 case Some{e} graph: T.CoreResult{graph,Some{e}} 59 case None{} T.Graph{edges,inverses}: T.CoreResult{T.Graph{edges,replace_inverse_erased(S,inverses,d,parent,child_ids(children))},None{}} 60 def reorder_erased(-S: Data,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+children: List<&2,T.ChildStatus>,parentLive: Bool) -> T.CoreResult: 61 checked_erased(S,validate_erased(S,parentLive,graph,d,parent,children),graph,d,parent,children) 62 # Public schema-specialized entry points delegate to the same erased algorithms. 63 def children_checked(~S: Data,children: List<&2,T.ChildStatus>,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+seen: List<&2,K.Key>) -> Maybe<&2,T.Error>: 64 children_checked_erased(S,children,graph,d,parent,seen) 65 def validate(~S: Data,parentLive: Bool,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+children: List<&2,T.ChildStatus>) -> Maybe<&2,T.Error>: 66 validate_erased(S,parentLive,graph,d,parent,children) 67 def replaced(~S: Data,found: Bool,entry: T.Inverse,sources: List<&2,K.Key>,rest: List<&2,T.Inverse>) -> List<&2,T.Inverse>: 68 replaced_erased(S,found,entry,sources,rest) 69 def replace_inverse(~S: Data,entries: List<&2,T.Inverse>,+d: T.Descriptor,+parent: K.Key,+sources: List<&2,K.Key>) -> List<&2,T.Inverse>: 70 replace_inverse_erased(S,entries,d,parent,sources) 71 def checked(~S: Data,error: Maybe<&2,T.Error>,graph: T.Graph,d: T.Descriptor,parent: K.Key,children: List<&2,T.ChildStatus>) -> T.CoreResult: 72 checked_erased(S,error,graph,d,parent,children) 73 def reorder(~S: Data,+graph: T.Graph,+d: T.Descriptor,+parent: K.Key,+children: List<&2,T.ChildStatus>,parentLive: Bool) -> T.CoreResult: 74 reorder_erased(S,graph,d,parent,children,parentLive)