1use crate::checker::{NamedKind, Ty, TyId, Types};
18use bynk_syntax::ast::BaseType;
19
20#[derive(Debug, Clone, Copy)]
22pub struct KernelMethod {
23 pub name: &'static str,
24 pub signature: &'static str,
25}
26
27const fn m(name: &'static str, signature: &'static str) -> KernelMethod {
28 KernelMethod { name, signature }
29}
30
31pub const LIST_METHODS: &[KernelMethod] = &[
33 m("length", "length() -> Int"),
34 m("get", "get(index: Int) -> Option[T]"),
35 m("prepend", "prepend(item: T) -> List[T]"),
36 m("fold", "fold(init: U, step: (U, T) -> U) -> U"),
37 m(
38 "foldEff",
39 "foldEff(init: U, step: (U, T) -> Effect[U]) -> Effect[U]",
40 ),
41 m("forEach", "forEach(f: T -> Effect[()]) -> Effect[()]"),
43 m(
45 "parTraverse",
46 "parTraverse(f: T -> Effect[()]) -> Effect[()]",
47 ),
48 m(
51 "traverseAll",
52 "traverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
53 ),
54 m(
55 "parTraverseAll",
56 "parTraverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
57 ),
58 m(
60 "traverseTry",
61 "traverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
62 ),
63 m(
64 "parTraverseTry",
65 "parTraverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
66 ),
67 m("map", "map(f: T -> U) -> List[U]"),
69 m("filter", "filter(p: T -> Bool) -> List[T]"),
70 m("flatMap", "flatMap(f: T -> List[U]) -> List[U]"),
71 m("sortBy", "sortBy(key: T -> K) -> List[T]"),
72 m("take", "take(n: Int) -> List[T]"),
73 m("skip", "skip(n: Int) -> List[T]"),
74 m("distinct", "distinct() -> List[T]"),
75 m("distinctBy", "distinctBy(key: T -> K) -> List[T]"),
76 m("count", "count() -> Int"),
77 m("any", "any(p: T -> Bool) -> Bool"),
78 m("all", "all(p: T -> Bool) -> Bool"),
79 m("first", "first() -> Option[T]"),
80 m("firstOrElse", "firstOrElse(default: T) -> T"),
81 m("sum", "sum(key: T -> K) -> K"),
82 m("min", "min(key: T -> K) -> Option[K]"),
83 m("max", "max(key: T -> K) -> Option[K]"),
84 m("average", "average(key: T -> K) -> Option[Float]"),
85 m(
88 "joinOn",
89 "joinOn(other: List[U], on: (T, U) -> Bool, into: (T, U) -> V) -> List[V]",
90 ),
91 m(
92 "leftJoin",
93 "leftJoin(other: List[U], on: (T, U) -> Bool, into: (T, Option[U]) -> V) -> List[V]",
94 ),
95 m(
96 "join",
97 "join(other: List[U], on: (T, U) -> Bool, into: (T, U) -> V) -> List[V]",
98 ),
99 m(
100 "groupBy",
101 "groupBy(key: T -> K, into: (K, List[T]) -> V) -> List[V]",
102 ),
103];
104
105pub const MAP_METHODS: &[KernelMethod] = &[
107 m("length", "length() -> Int"),
108 m("keys", "keys() -> List[K]"),
109 m("values", "values() -> List[V]"),
110 m("get", "get(key: K) -> Option[V]"),
111 m("insert", "insert(key: K, value: V) -> Map[K, V]"),
112];
113
114pub const QUERY_METHODS: &[KernelMethod] = &[
123 m("map", "map(f: T -> U) -> Query[U]"),
124 m("filter", "filter(p: T -> Bool) -> Query[T]"),
125 m("flatMap", "flatMap(f: T -> Query[U]) -> Query[U]"),
126 m("sortBy", "sortBy(key: T -> K) -> Query[T]"),
127 m("take", "take(n: Int) -> Query[T]"),
128 m("skip", "skip(n: Int) -> Query[T]"),
129 m("distinct", "distinct() -> Query[T]"),
130 m("distinctBy", "distinctBy(key: T -> K) -> Query[T]"),
131 m(
132 "joinOn",
133 "joinOn(other: Query[U], on: (T, U) -> Bool, into: (T, U) -> V) -> Query[V]",
134 ),
135 m(
136 "leftJoin",
137 "leftJoin(other: Query[U], on: (T, U) -> Bool, into: (T, Option[U]) -> V) -> Query[V]",
138 ),
139 m(
140 "join",
141 "join(other: Query[U], on: (T, U) -> Bool, into: (T, U) -> V) -> Query[V]",
142 ),
143 m(
144 "groupBy",
145 "groupBy(key: T -> K, into: (K, Query[T]) -> V) -> Query[V]",
146 ),
147 m("collect", "collect() -> Effect[List[T]]"),
148 m("first", "first() -> Effect[Option[T]]"),
149 m("firstOrElse", "firstOrElse(default: T) -> Effect[T]"),
150 m("count", "count() -> Effect[Int]"),
151 m("fold", "fold(init: U, step: (U, T) -> U) -> Effect[U]"),
152 m("any", "any(p: T -> Bool) -> Effect[Bool]"),
153 m("all", "all(p: T -> Bool) -> Effect[Bool]"),
154 m("sum", "sum(key: T -> K) -> Effect[K]"),
155 m("min", "min(key: T -> K) -> Effect[Option[K]]"),
156 m("max", "max(key: T -> K) -> Effect[Option[K]]"),
157 m("average", "average(key: T -> K) -> Effect[Option[Float]]"),
158 m("forEach", "forEach(f: T -> Effect[()]) -> Effect[()]"),
159 m(
160 "parTraverse",
161 "parTraverse(f: T -> Effect[()]) -> Effect[()]",
162 ),
163 m(
164 "traverseAll",
165 "traverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
166 ),
167 m(
168 "parTraverseAll",
169 "parTraverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
170 ),
171 m(
172 "traverseTry",
173 "traverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
174 ),
175 m(
176 "parTraverseTry",
177 "parTraverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
178 ),
179];
180
181pub const OPTION_METHODS: &[KernelMethod] = &[
183 m("map", "map(f: T -> U) -> Option[U]"),
184 m("andThen", "andThen(f: T -> Option[U]) -> Option[U]"),
185 m("getOrElse", "getOrElse(default: T) -> T"),
186 m("isSome", "isSome() -> Bool"),
187 m("okOr", "okOr(err: E) -> Result[T, E]"),
188];
189
190pub const RESULT_METHODS: &[KernelMethod] = &[
192 m("map", "map(f: T -> U) -> Result[U, E]"),
193 m("andThen", "andThen(f: T -> Result[U, E]) -> Result[U, E]"),
194 m("mapErr", "mapErr(f: E -> F) -> Result[T, F]"),
195 m("getOrElse", "getOrElse(default: T) -> T"),
196 m("isOk", "isOk() -> Bool"),
197];
198pub const EFFECT_RESULT_METHODS: &[KernelMethod] = &[
204 m("mapOk", "mapOk(f: T -> U) -> Effect[Result[U, E]]"),
205 m("mapErr", "mapErr(f: E -> F) -> Effect[Result[T, F]]"),
206 m(
207 "flatMapOk",
208 "flatMapOk(f: T -> Effect[Result[U, E]]) -> Effect[Result[U, E]]",
209 ),
210 m(
211 "flatMapErr",
212 "flatMapErr(f: E -> Effect[Result[T, F]]) -> Effect[Result[T, F]]",
213 ),
214];
215
216pub const STRING_METHODS: &[KernelMethod] = &[
218 m("length", "length() -> Int"),
219 m("split", "split(sep: String) -> List[String]"),
220 m("trim", "trim() -> String"),
221 m("toUpper", "toUpper() -> String"),
222 m("toLower", "toLower() -> String"),
223 m("contains", "contains(s: String) -> Bool"),
224 m("startsWith", "startsWith(s: String) -> Bool"),
225 m("endsWith", "endsWith(s: String) -> Bool"),
226 m("replace", "replace(from: String, to: String) -> String"),
227 m("slice", "slice(start: Int, end: Int) -> String"),
228 m("indexOf", "indexOf(s: String) -> Option[Int]"),
229 m("chars", "chars() -> List[String]"),
230 m("concat", "concat(s: String) -> String"),
231];
232
233pub const INT_METHODS: &[KernelMethod] = &[
235 m("toFloat", "toFloat() -> Float"),
236 m("toString", "toString() -> String"),
237 m("abs", "abs() -> Int"),
238 m("min", "min(other: Int) -> Int"),
239 m("max", "max(other: Int) -> Int"),
240 m("clamp", "clamp(lo: Int, hi: Int) -> Int"),
241];
242
243pub const FLOAT_METHODS: &[KernelMethod] = &[
245 m("round", "round() -> Int"),
246 m("floor", "floor() -> Int"),
247 m("ceil", "ceil() -> Int"),
248 m("truncate", "truncate() -> Int"),
249 m("toString", "toString() -> String"),
250 m("abs", "abs() -> Float"),
251 m("min", "min(other: Float) -> Float"),
252 m("max", "max(other: Float) -> Float"),
253 m("clamp", "clamp(lo: Float, hi: Float) -> Float"),
254 m("isNaN", "isNaN() -> Bool"),
255 m("isFinite", "isFinite() -> Bool"),
256];
257
258pub const DURATION_METHODS: &[KernelMethod] = &[
261 m("toMillis", "toMillis() -> Int"),
262 m("toString", "toString() -> String"),
263];
264
265pub const INSTANT_METHODS: &[KernelMethod] = &[
268 m("toEpochMillis", "toEpochMillis() -> Int"),
269 m("toString", "toString() -> String"),
270];
271
272pub const BYTES_METHODS: &[KernelMethod] = &[
276 m("length", "length() -> Int"),
277 m("toBase64", "toBase64() -> String"),
278 m("decodeUtf8", "decodeUtf8() -> Option[String]"),
279];
280
281pub fn methods_for(ty: TyId, tys: &Types) -> &'static [KernelMethod] {
285 match &*tys.get(ty) {
286 Ty::Base(BaseType::Int) => INT_METHODS,
287 Ty::Base(BaseType::Float) => FLOAT_METHODS,
288 Ty::Base(BaseType::Duration) => DURATION_METHODS,
289 Ty::Base(BaseType::Instant) => INSTANT_METHODS,
290 Ty::Base(BaseType::Bytes) => BYTES_METHODS,
291 Ty::Base(BaseType::String) => STRING_METHODS,
292 Ty::List(_) => LIST_METHODS,
293 Ty::Map(_, _) => MAP_METHODS,
294 Ty::Query(_) => QUERY_METHODS,
295 Ty::Option(_) => OPTION_METHODS,
296 Ty::Result(_, _) => RESULT_METHODS,
297 Ty::Effect(inner) if matches!(&*tys.get(*inner), Ty::Result(_, _)) => EFFECT_RESULT_METHODS,
300 Ty::Named {
305 kind: NamedKind::Refined(base),
306 ..
307 } => match base {
308 BaseType::Int => INT_METHODS,
309 BaseType::Float => FLOAT_METHODS,
310 BaseType::Duration => DURATION_METHODS,
311 BaseType::Instant => INSTANT_METHODS,
312 BaseType::Bytes => BYTES_METHODS,
313 BaseType::String => STRING_METHODS,
314 BaseType::Bool => &[],
315 },
316 _ => &[],
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 fn refined(tys: &Types, base: BaseType) -> TyId {
325 tys.intern(Ty::Named {
326 name: "R".to_string(),
327 kind: NamedKind::Refined(base),
328 args: Vec::new(),
329 })
330 }
331
332 #[test]
333 fn refined_receiver_inherits_base_kernel_methods() {
334 let tys = &Types::new();
335 assert_eq!(
337 methods_for(refined(tys, BaseType::String), tys).len(),
338 STRING_METHODS.len()
339 );
340 assert_eq!(
341 methods_for(refined(tys, BaseType::Int), tys).len(),
342 INT_METHODS.len()
343 );
344 assert_eq!(
345 methods_for(refined(tys, BaseType::Float), tys).len(),
346 FLOAT_METHODS.len()
347 );
348 assert!(methods_for(refined(tys, BaseType::Bool), tys).is_empty());
350 let opaque = tys.intern(Ty::Named {
352 name: "O".to_string(),
353 kind: NamedKind::Opaque(BaseType::String),
354 args: Vec::new(),
355 });
356 assert!(methods_for(opaque, tys).is_empty());
357 }
358
359 #[test]
364 fn query_receiver_offers_the_query_vocabulary() {
365 let tys = &Types::new();
366 let int = tys.intern(Ty::Base(BaseType::Int));
367 let query = tys.intern(Ty::Query(int));
368 assert_eq!(methods_for(query, tys).len(), QUERY_METHODS.len());
369 assert!(methods_for(query, tys).iter().any(|m| m.name == "filter"));
370 assert!(methods_for(query, tys).iter().any(|m| m.name == "collect"));
371 }
372}