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("append", "append(item: T) -> List[T]"),
38 m("concat", "concat(other: List[T]) -> List[T]"),
39 m("fold", "fold(init: U, step: (U, T) -> U) -> U"),
40 m(
41 "foldEff",
42 "foldEff(init: U, step: (U, T) -> Effect[U]) -> Effect[U]",
43 ),
44 m("forEach", "forEach(f: T -> Effect[()]) -> Effect[()]"),
46 m(
48 "parTraverse",
49 "parTraverse(f: T -> Effect[()]) -> Effect[()]",
50 ),
51 m(
54 "traverseAll",
55 "traverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
56 ),
57 m(
58 "parTraverseAll",
59 "parTraverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
60 ),
61 m(
63 "traverseTry",
64 "traverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
65 ),
66 m(
67 "parTraverseTry",
68 "parTraverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
69 ),
70 m("map", "map(f: T -> U) -> List[U]"),
72 m("filter", "filter(p: T -> Bool) -> List[T]"),
73 m("flatMap", "flatMap(f: T -> List[U]) -> List[U]"),
74 m("sortBy", "sortBy(key: T -> K) -> List[T]"),
75 m("take", "take(n: Int) -> List[T]"),
76 m("skip", "skip(n: Int) -> List[T]"),
77 m("distinct", "distinct() -> List[T]"),
78 m("distinctBy", "distinctBy(key: T -> K) -> List[T]"),
79 m("count", "count() -> Int"),
80 m("any", "any(p: T -> Bool) -> Bool"),
81 m("all", "all(p: T -> Bool) -> Bool"),
82 m("first", "first() -> Option[T]"),
83 m("firstOrElse", "firstOrElse(default: T) -> T"),
84 m("sum", "sum(key: T -> K) -> K"),
85 m("min", "min(key: T -> K) -> Option[K]"),
86 m("max", "max(key: T -> K) -> Option[K]"),
87 m("average", "average(key: T -> K) -> Option[Float]"),
88 m(
91 "joinOn",
92 "joinOn(other: List[U], on: (T, U) -> Bool, into: (T, U) -> V) -> List[V]",
93 ),
94 m(
95 "leftJoin",
96 "leftJoin(other: List[U], on: (T, U) -> Bool, into: (T, Option[U]) -> V) -> List[V]",
97 ),
98 m(
99 "join",
100 "join(other: List[U], on: (T, U) -> Bool, into: (T, U) -> V) -> List[V]",
101 ),
102 m(
103 "groupBy",
104 "groupBy(key: T -> K, into: (K, List[T]) -> V) -> List[V]",
105 ),
106];
107
108pub const MAP_METHODS: &[KernelMethod] = &[
110 m("length", "length() -> Int"),
111 m("keys", "keys() -> List[K]"),
112 m("values", "values() -> List[V]"),
113 m("get", "get(key: K) -> Option[V]"),
114 m("insert", "insert(key: K, value: V) -> Map[K, V]"),
115];
116
117pub const QUERY_METHODS: &[KernelMethod] = &[
126 m("map", "map(f: T -> U) -> Query[U]"),
127 m("filter", "filter(p: T -> Bool) -> Query[T]"),
128 m("flatMap", "flatMap(f: T -> Query[U]) -> Query[U]"),
129 m("sortBy", "sortBy(key: T -> K) -> Query[T]"),
130 m("take", "take(n: Int) -> Query[T]"),
131 m("skip", "skip(n: Int) -> Query[T]"),
132 m("distinct", "distinct() -> Query[T]"),
133 m("distinctBy", "distinctBy(key: T -> K) -> Query[T]"),
134 m(
135 "joinOn",
136 "joinOn(other: Query[U], on: (T, U) -> Bool, into: (T, U) -> V) -> Query[V]",
137 ),
138 m(
139 "leftJoin",
140 "leftJoin(other: Query[U], on: (T, U) -> Bool, into: (T, Option[U]) -> V) -> Query[V]",
141 ),
142 m(
143 "join",
144 "join(other: Query[U], on: (T, U) -> Bool, into: (T, U) -> V) -> Query[V]",
145 ),
146 m(
147 "groupBy",
148 "groupBy(key: T -> K, into: (K, Query[T]) -> V) -> Query[V]",
149 ),
150 m("collect", "collect() -> Effect[List[T]]"),
151 m("first", "first() -> Effect[Option[T]]"),
152 m("firstOrElse", "firstOrElse(default: T) -> Effect[T]"),
153 m("count", "count() -> Effect[Int]"),
154 m("fold", "fold(init: U, step: (U, T) -> U) -> Effect[U]"),
155 m("any", "any(p: T -> Bool) -> Effect[Bool]"),
156 m("all", "all(p: T -> Bool) -> Effect[Bool]"),
157 m("sum", "sum(key: T -> K) -> Effect[K]"),
158 m("min", "min(key: T -> K) -> Effect[Option[K]]"),
159 m("max", "max(key: T -> K) -> Effect[Option[K]]"),
160 m("average", "average(key: T -> K) -> Effect[Option[Float]]"),
161 m("forEach", "forEach(f: T -> Effect[()]) -> Effect[()]"),
162 m(
163 "parTraverse",
164 "parTraverse(f: T -> Effect[()]) -> Effect[()]",
165 ),
166 m(
167 "traverseAll",
168 "traverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
169 ),
170 m(
171 "parTraverseAll",
172 "parTraverseAll(f: T -> Effect[Result[U, E]]) -> Effect[List[Result[U, E]]]",
173 ),
174 m(
175 "traverseTry",
176 "traverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
177 ),
178 m(
179 "parTraverseTry",
180 "parTraverseTry(f: T -> Effect[Result[U, E]]) -> Effect[Result[List[U], E]]",
181 ),
182];
183
184pub const OPTION_METHODS: &[KernelMethod] = &[
186 m("map", "map(f: T -> U) -> Option[U]"),
187 m("andThen", "andThen(f: T -> Option[U]) -> Option[U]"),
188 m("getOrElse", "getOrElse(default: T) -> T"),
189 m("isSome", "isSome() -> Bool"),
190 m("okOr", "okOr(err: E) -> Result[T, E]"),
191];
192
193pub const RESULT_METHODS: &[KernelMethod] = &[
195 m("map", "map(f: T -> U) -> Result[U, E]"),
196 m("andThen", "andThen(f: T -> Result[U, E]) -> Result[U, E]"),
197 m("mapErr", "mapErr(f: E -> F) -> Result[T, F]"),
198 m("getOrElse", "getOrElse(default: T) -> T"),
199 m("isOk", "isOk() -> Bool"),
200];
201pub const EFFECT_RESULT_METHODS: &[KernelMethod] = &[
207 m("mapOk", "mapOk(f: T -> U) -> Effect[Result[U, E]]"),
208 m("mapErr", "mapErr(f: E -> F) -> Effect[Result[T, F]]"),
209 m(
210 "flatMapOk",
211 "flatMapOk(f: T -> Effect[Result[U, E]]) -> Effect[Result[U, E]]",
212 ),
213 m(
214 "flatMapErr",
215 "flatMapErr(f: E -> Effect[Result[T, F]]) -> Effect[Result[T, F]]",
216 ),
217];
218
219pub const STRING_METHODS: &[KernelMethod] = &[
221 m("length", "length() -> Int"),
222 m("split", "split(sep: String) -> List[String]"),
223 m("trim", "trim() -> String"),
224 m("toUpper", "toUpper() -> String"),
225 m("toLower", "toLower() -> String"),
226 m("contains", "contains(s: String) -> Bool"),
227 m("startsWith", "startsWith(s: String) -> Bool"),
228 m("endsWith", "endsWith(s: String) -> Bool"),
229 m("replace", "replace(from: String, to: String) -> String"),
230 m("slice", "slice(start: Int, end: Int) -> String"),
231 m("indexOf", "indexOf(s: String) -> Option[Int]"),
232 m("chars", "chars() -> List[String]"),
233 m("concat", "concat(s: String) -> String"),
234];
235
236pub const INT_METHODS: &[KernelMethod] = &[
238 m("toFloat", "toFloat() -> Float"),
239 m("toString", "toString() -> String"),
240 m("abs", "abs() -> Int"),
241 m("min", "min(other: Int) -> Int"),
242 m("max", "max(other: Int) -> Int"),
243 m("clamp", "clamp(lo: Int, hi: Int) -> Int"),
244];
245
246pub const FLOAT_METHODS: &[KernelMethod] = &[
248 m("round", "round() -> Int"),
249 m("floor", "floor() -> Int"),
250 m("ceil", "ceil() -> Int"),
251 m("truncate", "truncate() -> Int"),
252 m("toString", "toString() -> String"),
253 m("abs", "abs() -> Float"),
254 m("min", "min(other: Float) -> Float"),
255 m("max", "max(other: Float) -> Float"),
256 m("clamp", "clamp(lo: Float, hi: Float) -> Float"),
257 m("isNaN", "isNaN() -> Bool"),
258 m("isFinite", "isFinite() -> Bool"),
259];
260
261pub const DURATION_METHODS: &[KernelMethod] = &[
264 m("toMillis", "toMillis() -> Int"),
265 m("toString", "toString() -> String"),
266];
267
268pub const INSTANT_METHODS: &[KernelMethod] = &[
271 m("toEpochMillis", "toEpochMillis() -> Int"),
272 m("toString", "toString() -> String"),
273];
274
275pub const BYTES_METHODS: &[KernelMethod] = &[
280 m("length", "length() -> Int"),
281 m("concat", "concat(other: Bytes) -> Bytes"),
282 m("slice", "slice(start: Int, end: Int) -> Bytes"),
283 m("compare", "compare(other: Bytes) -> Int"),
284 m("toHex", "toHex() -> String"),
285 m("toBase64", "toBase64() -> String"),
286 m("toBase64Url", "toBase64Url() -> String"),
287 m("decodeUtf8", "decodeUtf8() -> Option[String]"),
288];
289
290pub const REQUEST_HEADERS_METHODS: &[KernelMethod] = &[
293 m("get", "get(name: String) -> Option[String]"),
294 m("bearer", "bearer() -> Option[String]"),
295 m("clientIp", "clientIp() -> Option[String]"),
296];
297
298pub const TOKEN_CLAIMS_METHODS: &[KernelMethod] = &[
300 m("get", "get(name: String) -> Option[String]"),
301 m("has", "has(name: String) -> Bool"),
302];
303
304pub fn methods_for(ty: TyId, tys: &Types) -> &'static [KernelMethod] {
308 match &*tys.get(ty) {
309 Ty::Base(BaseType::Int) => INT_METHODS,
310 Ty::Base(BaseType::Float) => FLOAT_METHODS,
311 Ty::Base(BaseType::Duration) => DURATION_METHODS,
312 Ty::Base(BaseType::Instant) => INSTANT_METHODS,
313 Ty::Base(BaseType::Bytes) => BYTES_METHODS,
314 Ty::Base(BaseType::String) => STRING_METHODS,
315 Ty::RequestHeaders => REQUEST_HEADERS_METHODS,
316 Ty::TokenClaims => TOKEN_CLAIMS_METHODS,
317 Ty::List(_) => LIST_METHODS,
318 Ty::Map(_, _) => MAP_METHODS,
319 Ty::Query(_) => QUERY_METHODS,
320 Ty::Option(_) => OPTION_METHODS,
321 Ty::Result(_, _) => RESULT_METHODS,
322 Ty::Effect(inner) if matches!(&*tys.get(*inner), Ty::Result(_, _)) => EFFECT_RESULT_METHODS,
325 Ty::Named {
330 kind: NamedKind::Refined(base),
331 ..
332 } => match base {
333 BaseType::Int => INT_METHODS,
334 BaseType::Float => FLOAT_METHODS,
335 BaseType::Duration => DURATION_METHODS,
336 BaseType::Instant => INSTANT_METHODS,
337 BaseType::Bytes => BYTES_METHODS,
338 BaseType::String => STRING_METHODS,
339 BaseType::Bool => &[],
340 },
341 _ => &[],
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 fn refined(tys: &Types, base: BaseType) -> TyId {
350 tys.intern(Ty::Named {
351 name: "R".to_string(),
352 kind: NamedKind::Refined(base),
353 args: Vec::new(),
354 })
355 }
356
357 #[test]
358 fn refined_receiver_inherits_base_kernel_methods() {
359 let tys = &Types::new();
360 assert_eq!(
362 methods_for(refined(tys, BaseType::String), tys).len(),
363 STRING_METHODS.len()
364 );
365 assert_eq!(
366 methods_for(refined(tys, BaseType::Int), tys).len(),
367 INT_METHODS.len()
368 );
369 assert_eq!(
370 methods_for(refined(tys, BaseType::Float), tys).len(),
371 FLOAT_METHODS.len()
372 );
373 assert!(methods_for(refined(tys, BaseType::Bool), tys).is_empty());
375 let opaque = tys.intern(Ty::Named {
377 name: "O".to_string(),
378 kind: NamedKind::Opaque(BaseType::String),
379 args: Vec::new(),
380 });
381 assert!(methods_for(opaque, tys).is_empty());
382 }
383
384 #[test]
389 fn query_receiver_offers_the_query_vocabulary() {
390 let tys = &Types::new();
391 let int = tys.intern(Ty::Base(BaseType::Int));
392 let query = tys.intern(Ty::Query(int));
393 assert_eq!(methods_for(query, tys).len(), QUERY_METHODS.len());
394 assert!(methods_for(query, tys).iter().any(|m| m.name == "filter"));
395 assert!(methods_for(query, tys).iter().any(|m| m.name == "collect"));
396 }
397}