schema_fragments.bend
bendvy-ecs-core@0.1.0.3 · 187 lines · 40 definitions
1import Base
2
3
4
5
6type RegistryKind is Data:
7 Component{}
8 Resource{}
9 Event{}
10 Relation{}
11 Service{}
12
13
14type Descriptor<S: Data,Token: Data,Payload: Type> is Data:
15 Descriptor{kind: RegistryKind,key: String,name: String}
16
17
18type PersistenceKind is Data:
19 Plain{}
20 Transient{}
21 Constructed{}
22
23
24type Entry<S: Data> is Data:
25 Entry{kind: RegistryKind,key: String,name: String}
26 Classified{kind: RegistryKind,key: String,name: String,persistence:PersistenceKind}
27
28
29type Fragment<S: Data,Owners: Type> is Type:
30 Fragment{entries: List<&2,Entry<S>>,owners: Owners}
31
32
33type Error is Data:
34 DuplicateKey{kind: RegistryKind,key: String}
35 DuplicateName{kind: RegistryKind,name: String}
36 UndeclaredDescriptor{kind: RegistryKind,key: String,name: String}
37
38type Validation is Data:
39 Valid{}
40 Invalid{error: Error}
41
42
43type Merge<S: Data,L: Type,R: Type> is Type:
44 Merged{fragment: Fragment<S,L & R>}
45 Rejected{error: Error,left: Fragment<S,L>,right: Fragment<S,R>}
46
47
48type Bound<S: Data,Owners: Type,Output: Type> is Type:
49 Built{output: Output}
50 BindRejected{error: Error,fragment: Fragment<S,Owners>}
51
52
53def bare_entry(~S:Data,value:Entry<S>) -> Entry<S>:
54 match value:
55 case Entry{kind,key,name}:Entry{kind,key,name}
56 case Classified{kind,key,name,_}:Entry{kind,key,name}
57def bare_entries(~S:Data,values:List<&2,Entry<S>>) -> List<&2,Entry<S>>:
58 match values:
59 case []:[]
60 case head <> rest:bare_entry(~S,head) <> bare_entries(~S,rest)
61def same_kind(a: RegistryKind,b: RegistryKind) -> Bool:
62 match a b:
63 case Component{} Component{}: True{}
64 case Resource{} Resource{}: True{}
65 case Event{} Event{}: True{}
66 case Relation{} Relation{}: True{}
67 case Service{} Service{}: True{}
68 case _ _: False{}
69
70
71def entry(~S: Data,~Token: Data,~Payload: Type,descriptor: Descriptor<S,Token,Payload>) -> Entry<S>:
72 match descriptor:
73 case Descriptor{kind,key,name}: Entry{kind,key,name}
74
75
76def empty(~S: Data,owners: Unit) -> Fragment<S,Unit>:
77 Fragment{[],owners}
78
79
80
81def fragment(~S: Data,~Owners: Type,entries: List<&2,Entry<S>>,owners: Owners) -> Fragment<S,Owners>:
82 Fragment{entries,owners}
83def key_exists_bare(~S: Data,xs: List<&2,Entry<S>>,+kind: RegistryKind,+key: String) -> Bool:
84 match xs:
85 case Nil{}: False{}
86 case Con{Entry{other,otherKey,_},rest}: Bool.or(Bool.and(same_kind(kind,other),String.eq(key,otherKey)),key_exists_bare(~S,rest,kind,key))
87 case Con{Classified{other,otherKey,_,_},rest}: Bool.or(Bool.and(same_kind(kind,other),String.eq(key,otherKey)),key_exists_bare(~S,rest,kind,key))
88def key_exists(~S:Data,xs:List<&2,Entry<S>>,kind:RegistryKind,key:String) -> Bool:key_exists_bare(~S,bare_entries(~S,xs),kind,key)
89def name_exists_bare(~S: Data,xs: List<&2,Entry<S>>,+kind: RegistryKind,+name: String) -> Bool:
90 match xs:
91 case Nil{}: False{}
92 case Con{Entry{other,_,otherName},rest}: Bool.or(Bool.and(same_kind(kind,other),String.eq(name,otherName)),name_exists_bare(~S,rest,kind,name))
93 case Con{Classified{other,_,otherName,_},rest}: Bool.or(Bool.and(same_kind(kind,other),String.eq(name,otherName)),name_exists_bare(~S,rest,kind,name))
94def name_exists(~S:Data,xs:List<&2,Entry<S>>,kind:RegistryKind,name:String) -> Bool:name_exists_bare(~S,bare_entries(~S,xs),kind,name)
95def check_entry(keyConflict: Bool,nameConflict: Bool,kind: RegistryKind,key: String,name: String) -> Validation:
96 match keyConflict nameConflict:
97 case True{} _: Invalid{DuplicateKey{kind,key}}
98 case False{} True{}: Invalid{DuplicateName{kind,name}}
99 case False{} False{}: Valid{}
100def check_one_bare(~S: Data,+seen: List<&2,Entry<S>>,item: Entry<S>) -> Validation:
101 match item:
102 case Entry{+kind,+key,+name}: check_entry(key_exists(~S,seen,kind,key),name_exists(~S,seen,kind,name),kind,key,name)
103 case Classified{+kind,+key,+name,_}: check_entry(key_exists(~S,seen,kind,key),name_exists(~S,seen,kind,name),kind,key,name)
104def check_one(~S:Data,seen:List<&2,Entry<S>>,item:Entry<S>) -> Validation:check_one_bare(~S,bare_entries(~S,seen),bare_entry(~S,item))
105def validation_continue(~S: Data,result: Validation,next: Unit -> Validation) -> Validation:
106 match result:
107 case Invalid{error}: Invalid{error}
108 case Valid{}: next(Unit{})
109def validate_loop(~S: Data,xs: List<&2,Entry<S>>,+seen: List<&2,Entry<S>>) -> Validation:
110 match xs:
111 case Nil{}: Valid{}
112 case Con{+item,rest}: validation_continue(~S,check_one(~S,seen,item),_ => validate_loop(~S,rest,item <> seen))
113def filter_kind_done(~S: Data,selected: Bool,item: Entry<S>,rest: List<&2,Entry<S>>) -> List<&2,Entry<S>>:
114 match selected:
115 case True{}: item <> rest
116 case False{}: rest
117def filter_kind(~S:Data,xs:List<&2,Entry<S>>,+kind:RegistryKind) -> List<&2,Entry<S>>:
118 match xs:
119 case []:[]
120 case Entry{+itemKind,+key,+name} <> rest:filter_kind_done(~S,same_kind(kind,itemKind),Entry{itemKind,key,name},filter_kind(~S,rest,kind))
121 case Classified{+itemKind,+key,+name,+persistence} <> rest:filter_kind_done(~S,same_kind(kind,itemKind),Classified{itemKind,key,name,persistence},filter_kind(~S,rest,kind))
122def first_failure(first: Validation,second: Validation) -> Validation:
123 match first:
124 case Invalid{error}: Invalid{error}
125 case Valid{}: second
126
127
128
129def validate(~S: Data,+entries: List<&2,Entry<S>>) -> Validation:
130 first_failure(validate_loop(~S,filter_kind(~S,entries,Component{}),[]),first_failure(validate_loop(~S,filter_kind(~S,entries,Resource{}),[]),first_failure(validate_loop(~S,filter_kind(~S,entries,Event{}),[]),first_failure(validate_loop(~S,filter_kind(~S,entries,Relation{}),[]),validate_loop(~S,filter_kind(~S,entries,Service{}),[])))))
131def merge_checked(~S: Data,~L: Type,~R: Type,result: Validation,+entries: List<&2,Entry<S>>,left: Fragment<S,L>,right: Fragment<S,R>) -> Merge<S,L,R>:
132 match result left right:
133 case Invalid{error} left right: Rejected{error,left,right}
134 case Valid{} Fragment{_,l} Fragment{_,r}: Merged{Fragment{entries,(l,r)}}
135
136
137
138def merge(~S: Data,~L: Type,~R: Type,left: Fragment<S,L>,right: Fragment<S,R>) -> Merge<S,L,R>:
139 match left right:
140 case Fragment{+le,l} Fragment{+re,r}: merge_checked(~S,~L,~R,first_failure(validate(~S,le),first_failure(validate(~S,re),validate(~S,List.append(&2,Entry<S>,le,re)))),List.append(&2,Entry<S>,le,re),Fragment{le,l},Fragment{re,r})
141def bind_checked(~S: Data,~Owners: Type,~Output: Type,~build: Owners -> Output,result: Validation,fragment: Fragment<S,Owners>) -> Bound<S,Owners,Output>:
142 match result fragment:
143 case Invalid{error} fragment: BindRejected{error,fragment}
144 case Valid{} Fragment{_,owners}: Built{build(owners)}
145
146
147
148def bind(~S: Data,~Owners: Type,~Output: Type,~build: Owners -> Output,fragment: Fragment<S,Owners>) -> Bound<S,Owners,Output>:
149 match fragment:
150 case Fragment{+entries,owners}: bind_checked(~S,~Owners,~Output,~build,validate(~S,entries),Fragment{entries,owners})
151
152
153
154def descriptor_equal(~S:Data,a:Entry<S>,b:Entry<S>) -> Bool:
155 match a b:
156 case Entry{ak,ax,an} Entry{bk,bx,bn}:Bool.and(same_kind(ak,bk),Bool.and(String.eq(ax,bx),String.eq(an,bn)))
157 case _ _:False{}
158def descriptor_present(~S:Data,xs:List<&2,Entry<S>>,+item:Entry<S>) -> Bool:
159 match xs:
160 case []:False{}
161 case head <> rest:Bool.or(descriptor_equal(~S,bare_entry(~S,head),bare_entry(~S,item)),descriptor_present(~S,rest,item))
162def required_continue(result: Validation,next: Unit -> Validation) -> Validation:
163 match result:
164 case Invalid{error}: Invalid{error}
165 case Valid{}: next(Unit{})
166def required_check_bare(~S: Data,present: Bool,item: Entry<S>) -> Validation:
167 match present item:
168 case True{} _: Valid{}
169 case False{} Entry{kind,key,name}: Invalid{UndeclaredDescriptor{kind,key,name}}
170 case False{} Classified{kind,key,name,_}: Invalid{UndeclaredDescriptor{kind,key,name}}
171def required_check(~S:Data,present:Bool,item:Entry<S>) -> Validation:required_check_bare(~S,present,bare_entry(~S,item))
172def requirements(~S: Data,required: List<&2,Entry<S>>,+entries: List<&2,Entry<S>>) -> Validation:
173 match required:
174 case []: Valid{}
175 case +item <> rest: required_continue(required_check(~S,descriptor_present(~S,entries,item),item),_ => requirements(~S,rest,entries))
176def bind_requirements(~S: Data,~Owners: Type,~Output: Type,~build: Owners -> Output,result: Validation,required: List<&2,Entry<S>>,fragment: Fragment<S,Owners>) -> Bound<S,Owners,Output>:
177 match result fragment:
178 case Invalid{error} fragment: BindRejected{error,fragment}
179 case Valid{} Fragment{+entries,owners}: bind_checked(~S,~Owners,~Output,~build,requirements(~S,required,entries),Fragment{entries,owners})
180
181
182
183
184
185def bind_declared(~S: Data,~Owners: Type,~Output: Type,~build: Owners -> Output,fragment: Fragment<S,Owners>,required: List<&2,Entry<S>>) -> Bound<S,Owners,Output>:
186 match fragment:
187 case Fragment{+entries,owners}: bind_requirements(~S,~Owners,~Output,~build,validate(~S,entries),required,Fragment{entries,owners})