1pub use byte_code::*;
2pub use compiler_output::*;
3pub use ir::*;
4pub use type_with_unit::*;
5pub use worker_functions_in_rib::*;
6
7use crate::rib_type_error::RibTypeError;
8use crate::wit_type::{TypeEnum, TypeVariant};
9use crate::{
10 ComponentDependency, CustomInstanceSpec, Expr, GlobalVariableTypeSpec, InferredExpr,
11 RibInputTypeInfo, RibOutputTypeInfo,
12};
13use std::error::Error;
14use std::fmt::Display;
15
16mod byte_code;
17mod compiler_output;
18mod desugar;
19mod ir;
20mod type_with_unit;
21mod worker_functions_in_rib;
22
23#[derive(Default)]
24pub struct RibCompiler {
25 component: ComponentDependency,
26 global_variable_type_spec: Vec<GlobalVariableTypeSpec>,
27 custom_instance_spec: Vec<CustomInstanceSpec>,
28}
29
30impl RibCompiler {
31 pub fn new(config: RibCompilerConfig) -> RibCompiler {
32 let global_variable_type_spec = config.input_spec;
33
34 RibCompiler {
35 component: config.component,
36 global_variable_type_spec,
37 custom_instance_spec: config.custom_instance_spec,
38 }
39 }
40
41 pub fn infer_types(&self, expr: Expr) -> Result<InferredExpr, RibCompilationError> {
42 let _infer_profile = crate::profile::InferOnlyProfileGuard::new();
43
44 let expr_for_infer = { expr.clone() };
45
46 let result = {
47 let _p = crate::profile::Scope::new(
48 "compile: RibCompiler.infer_types InferredExpr::from_expr",
49 );
50 InferredExpr::from_expr(
51 expr_for_infer,
52 &self.component,
53 &self.global_variable_type_spec,
54 &self.custom_instance_spec,
55 )
56 };
57 result.map_err(|err| {
58 let rib_type_error = RibTypeError::from_rib_type_error_internal(err, expr);
59 RibCompilationError::RibTypeError(Box::new(rib_type_error))
60 })
61 }
62
63 pub fn get_custom_instance_names(&self) -> Vec<String> {
64 self.custom_instance_spec
65 .iter()
66 .map(|spec| spec.instance_name.clone())
67 .collect::<Vec<_>>()
68 }
69
70 pub fn get_component_dependency(&self) -> ComponentDependency {
71 self.component.clone()
72 }
73
74 pub fn compile(&self, expr: Expr) -> Result<CompilerOutput, RibCompilationError> {
75 let _compile_profile = crate::profile::CompileProfileGuard::enter();
76 let inferred_expr = {
77 let _p = crate::profile::Scope::new("compile: RibCompiler.compile infer_types (total)");
78 self.infer_types(expr)?
79 };
80
81 let function_calls_identified = {
82 let _p = crate::profile::Scope::new("compile: identity function call");
83 WorkerFunctionsInRib::from_inferred_expr(&inferred_expr, &self.component)?
84 };
85
86 let global_input_type_info = {
87 let _p = crate::profile::Scope::new("compile: identify input types of rib");
88 RibInputTypeInfo::from_expr(&inferred_expr)?
89 };
90 let output_type_info = {
91 let _p = crate::profile::Scope::new("compile: identify output types of rib");
92 RibOutputTypeInfo::from_expr(&inferred_expr)?
93 };
94
95 let allowed_global_variables: Vec<String> = self
97 .global_variable_type_spec
98 .iter()
99 .map(|x| x.variable())
100 .collect::<Vec<_>>();
101
102 let mut unidentified_global_inputs = vec![];
103
104 if !allowed_global_variables.is_empty() {
105 for (name, _) in global_input_type_info.types.iter() {
106 if !allowed_global_variables.contains(name) {
107 unidentified_global_inputs.push(name.clone());
108 }
109 }
110 }
111
112 if !unidentified_global_inputs.is_empty() {
113 return Err(RibCompilationError::UnsupportedGlobalInput {
114 invalid_global_inputs: unidentified_global_inputs,
115 valid_global_inputs: allowed_global_variables,
116 });
117 }
118
119 let byte_code = {
120 let _p = crate::profile::Scope::new("compile: byte code generation");
121 RibByteCode::from_expr(&inferred_expr)?
122 };
123
124 Ok(CompilerOutput {
125 worker_invoke_calls: function_calls_identified,
126 byte_code,
127 rib_input_type_info: global_input_type_info,
128 rib_output_type_info: Some(output_type_info),
129 })
130 }
131
132 pub fn get_variants(&self) -> Vec<TypeVariant> {
133 self.component.get_variants()
134 }
135
136 pub fn get_enums(&self) -> Vec<TypeEnum> {
137 self.component.get_enums()
138 }
139}
140
141#[derive(Default)]
156pub struct RibCompilerConfig {
157 pub component: ComponentDependency,
158 input_spec: Vec<GlobalVariableTypeSpec>,
159 custom_instance_spec: Vec<CustomInstanceSpec>,
160}
161
162impl RibCompilerConfig {
163 pub fn new(
164 component: ComponentDependency,
165 input_spec: Vec<GlobalVariableTypeSpec>,
166 custom_instance_spec: Vec<CustomInstanceSpec>,
167 ) -> RibCompilerConfig {
168 RibCompilerConfig {
169 component,
170 input_spec,
171 custom_instance_spec,
172 }
173 }
174}
175
176pub trait GenerateInstanceName {
177 fn generate_instance_name(&self) -> String;
178}
179
180pub struct DefaultWorkerNameGenerator;
181
182impl GenerateInstanceName for DefaultWorkerNameGenerator {
183 fn generate_instance_name(&self) -> String {
184 let uuid = uuid::Uuid::new_v4();
185 format!("instance-{uuid}")
186 }
187}
188
189#[derive(Debug, Clone, PartialEq)]
190pub enum RibCompilationError {
191 ByteCodeGenerationFail(Box<RibByteCodeGenerationError>),
195
196 RibTypeError(Box<RibTypeError>),
199
200 InvalidSyntax(String),
202
203 UnsupportedGlobalInput {
213 invalid_global_inputs: Vec<String>,
214 valid_global_inputs: Vec<String>,
215 },
216
217 RibStaticAnalysisError(String),
220}
221
222impl From<RibByteCodeGenerationError> for RibCompilationError {
223 fn from(err: RibByteCodeGenerationError) -> Self {
224 RibCompilationError::ByteCodeGenerationFail(Box::new(err))
225 }
226}
227
228impl From<RibTypeError> for RibCompilationError {
229 fn from(err: RibTypeError) -> Self {
230 RibCompilationError::RibTypeError(Box::new(err))
231 }
232}
233
234impl Display for RibCompilationError {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 match self {
237 RibCompilationError::RibStaticAnalysisError(msg) => {
238 write!(f, "rib static analysis error: {msg}")
239 }
240 RibCompilationError::RibTypeError(err) => write!(f, "{err}"),
241 RibCompilationError::InvalidSyntax(msg) => write!(f, "invalid rib syntax: {msg}"),
242 RibCompilationError::UnsupportedGlobalInput {
243 invalid_global_inputs,
244 valid_global_inputs,
245 } => {
246 write!(
247 f,
248 "unsupported global input variables: {}. expected: {}",
249 invalid_global_inputs.join(", "),
250 valid_global_inputs.join(", ")
251 )
252 }
253 RibCompilationError::ByteCodeGenerationFail(e) => {
254 write!(f, "{e}")
255 }
256 }
257 }
258}
259
260impl Error for RibCompilationError {}
261
262#[cfg(test)]
263mod compiler_error_tests {
264 mod type_mismatch_errors {
265 use test_r::test;
266
267 use crate::compiler::compiler_error_tests::test_utils;
268 use crate::compiler::compiler_error_tests::test_utils::strip_spaces;
269 use crate::{Expr, RibCompiler, RibCompilerConfig};
270
271 #[test]
272 async fn test_invalid_pattern_match0() {
273 let expr = r#"
274 match 1 {
275 1 => { foo : "bar" },
276 2 => { foo : 1 }
277 }
278
279 "#;
280
281 let expr = Expr::from_text(expr).unwrap();
282
283 let metadata = test_utils::get_metadata();
284
285 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
286 let error_msg = compiler.compile(expr).unwrap_err().to_string();
287
288 let expected = r#"
289 error in the following rib found at line 3, column 28
290 `"bar"`
291 cause: type mismatch. expected s32, found string
292 the expression `"bar"` is inferred as `string` by default
293 "#;
294
295 assert_eq!(error_msg, test_utils::strip_spaces(expected));
296 }
297
298 #[test]
299 async fn test_invalid_pattern_match1() {
300 let expr = r#"
301 let x = 1;
302 match some(x) {
303 some(_) => {foo: x},
304 none => {foo: "bar"}
305 }
306 "#;
307
308 let expr = Expr::from_text(expr).unwrap();
309
310 let metadata = test_utils::get_metadata();
311
312 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
313 let error_msg = compiler.compile(expr).unwrap_err().to_string();
314
315 let expected = r#"
316 error in the following rib found at line 2, column 19
317 `1`
318 cause: type mismatch. expected string, found s32
319 the expression `1` is inferred as `s32` by default
320 "#;
321
322 assert_eq!(error_msg, test_utils::strip_spaces(expected));
323 }
324
325 #[test]
326 async fn test_invalid_pattern_match2() {
327 let expr = r#"
328 let x: option<u64> = some(1);
329 match x {
330 some(x) => ok(x),
331 none => ok("none")
332 }
333 "#;
334
335 let expr = Expr::from_text(expr).unwrap();
336
337 let metadata = test_utils::get_metadata();
338
339 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
340 let error_msg = compiler.compile(expr).unwrap_err().to_string();
341
342 let expected = r#"
343 error in the following rib found at line 5, column 27
344 `"none"`
345 cause: type mismatch. expected u64, found string
346 expected type u64 based on expression `x` found at line 4 column 27
347 the expression `"none"` is inferred as `string` by default
348 "#;
349
350 assert_eq!(error_msg, test_utils::strip_spaces(expected));
351 }
352
353 #[test]
354 async fn test_invalid_pattern_match3() {
355 let expr = r#"
356 let x: option<u64> = some(1);
357 match x {
358 some(x) => ok("none"),
359 none => ok(1)
360 }
361 "#;
362
363 let expr = Expr::from_text(expr).unwrap();
364
365 let metadata = test_utils::get_metadata();
366
367 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
368 let error_msg = compiler.compile(expr).unwrap_err().to_string();
369
370 let expected = r#"
371 error in the following rib found at line 4, column 27
372 `"none"`
373 cause: type mismatch. expected s32, found string
374 expected type s32 based on expression `1` found at line 5 column 27
375 the expression `1` is inferred as `s32` by default
376 the expression `"none"` is inferred as `string` by default
377 "#;
378
379 assert_eq!(error_msg, test_utils::strip_spaces(expected));
380 }
381
382 #[test]
383 async fn test_invalid_pattern_match4() {
384 let expr = r#"
385 let x: s32 = 1;
386 let y: u64 = 2;
387
388 match some(1) {
389 some(_) => ok(x),
390 none => ok(y)
391 }
392 "#;
393
394 let expr = Expr::from_text(expr).unwrap();
395
396 let metadata = test_utils::get_metadata();
397
398 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
399 let error_msg = compiler.compile(expr).unwrap_err().to_string();
400
401 let expected = r#"
402 error in the following rib found at line 7, column 27
403 `y`
404 cause: type mismatch. expected s32, found u64
405 expected type s32 based on expression `x` found at line 6 column 27
406 the type of `x` is declared as `s32` at line 2 column 11
407 the type of `y` is declared as `u64` at line 3 column 11
408 "#;
409
410 assert_eq!(error_msg, test_utils::strip_spaces(expected));
411 }
412
413 #[test]
414 fn test_invalid_function_call0() {
415 let expr = r#"
416 let result = foo(1);
417 result
418 "#;
419
420 let expr = Expr::from_text(expr).unwrap();
421
422 let metadata = test_utils::get_metadata();
423
424 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
425 let error_msg = compiler.compile(expr).unwrap_err().to_string();
426
427 let expected = r#"
428 error in the following rib found at line 2, column 28
429 `1`
430 cause: type mismatch. expected record { a: record { aa: s32, ab: s32, ac: list<s32>, ad: record { ada: s32 }, ae: tuple<s32, string> }, b: u64, c: list<s32>, d: record { da: s32 } }, found s32
431 invalid argument to the function `foo`
432 "#;
433
434 assert_eq!(error_msg, test_utils::strip_spaces(expected));
435 }
436
437 #[test]
438 fn test_invalid_function_call1asdasd() {
439 let expr = r#"
440 let worker = instance("my-worker");
441 let result = worker.foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: "foo", c: [1, 2, 3], d: {da: 4}});
442 result
443 "#;
444
445 let expr = Expr::from_text(expr).unwrap();
446
447 let metadata = test_utils::get_metadata();
448
449 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
450 let error_msg = compiler.compile(expr).unwrap_err().to_string();
451
452 let expected = r#"
453 error in the following rib found at line 3, column 100
454 `"foo"`
455 cause: type mismatch. expected u64, found string
456 the expression `"foo"` is inferred as `string` by default
457 "#;
458
459 assert_eq!(error_msg, test_utils::strip_spaces(expected));
460 }
461
462 #[test]
463 fn test_invalid_function_call2() {
464 let expr = r#"
465 let worker = instance("my-worker");
466 let result = worker.foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: ["foo", "bar"], d: {da: 4}});
467 result
468 "#;
469
470 let expr = Expr::from_text(expr).unwrap();
471
472 let metadata = test_utils::get_metadata();
473
474 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
475 let error_msg = compiler.compile(expr).unwrap_err().to_string();
476
477 let expected = r#"
478 error in the following rib found at line 3, column 107
479 `"foo"`
480 cause: type mismatch. expected s32, found string
481 the expression `"foo"` is inferred as `string` by default
482 "#;
483
484 assert_eq!(error_msg, test_utils::strip_spaces(expected));
485 }
486
487 #[test]
488 fn test_invalid_function_call3() {
489 let expr = r#"
490 let worker = instance();
491 let result = worker.foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: "foo"}});
492 result
493 "#;
494
495 let expr = Expr::from_text(expr).unwrap();
496
497 let metadata = test_utils::get_metadata();
498
499 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
500 let error_msg = compiler.compile(expr).unwrap_err().to_string();
501
502 let expected = r#"
503 error in the following rib found at line 3, column 122
504 `"foo"`
505 cause: type mismatch. expected s32, found string
506 the expression `"foo"` is inferred as `string` by default
507 "#;
508
509 assert_eq!(error_msg, test_utils::strip_spaces(expected));
510 }
511
512 #[test]
517 fn test_invalid_function_call4() {
518 let expr = r#"
519 let result = foo({a: {aa: 1, ab: 2, ac: (1, 2), ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: 1}});
520 result
521 "#;
522
523 let expr = Expr::from_text(expr).unwrap();
524
525 let metadata = test_utils::get_metadata();
526
527 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
528 let error_msg = compiler.compile(expr).unwrap_err().to_string();
529
530 let expected = r#"
531 error in the following rib found at line 2, column 51
532 `(1, 2)`
533 cause: ambiguous types: `list<number>`, `tuple<number, number>`
534 "#;
535
536 assert_eq!(error_msg, test_utils::strip_spaces(expected));
537 }
538
539 #[test]
540 fn test_invalid_function_call5() {
541 let expr = r#"
542 let x = {a: "foo"};
543 let result = foo({a: {aa: 1, ab: 2, ac: x, ad: {ada: 1}, ae: (1, "foo")}, b: 2, c: [1, 2], d: {da: 1}});
544 result
545 "#;
546
547 let expr = Expr::from_text(expr).unwrap();
548
549 let metadata = test_utils::get_metadata();
550
551 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
552 let error_msg = compiler.compile(expr).unwrap_err().to_string();
553
554 let expected = r#"
555 error in the following rib found at line 2, column 21
556 `{a: "foo"}`
557 cause: ambiguous types: `list<number>`, `record{a: string}`
558 "#;
559
560 assert_eq!(error_msg, test_utils::strip_spaces(expected));
561 }
562
563 #[test]
564 fn test_invalid_function_call6() {
565 let expr = r#"
566 let worker = instance("my-worker");
567 let result = worker.foo({a: {aa: "foo", ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}});
568 result
569 "#;
570
571 let expr = Expr::from_text(expr).unwrap();
572
573 let metadata = test_utils::get_metadata();
574
575 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
576 let error_msg = compiler.compile(expr).unwrap_err().to_string();
577
578 let expected = r#"
579 error in the following rib found at line 3, column 44
580 `"foo"`
581 cause: type mismatch. expected s32, found string
582 the expression `"foo"` is inferred as `string` by default
583 "#;
584
585 assert_eq!(error_msg, test_utils::strip_spaces(expected));
586 }
587
588 #[test]
589 fn test_invalid_function_call7() {
590 let expr = r#"
591 let worker = instance();
592 let result = worker.foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: "1"}, ae: (1, "foo")}, b: 3, c: [1, 2, 3], d: {da: 4}});
593 result
594 "#;
595
596 let expr = Expr::from_text(expr).unwrap();
597
598 let metadata = test_utils::get_metadata();
599
600 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
601 let error_msg = compiler.compile(expr).unwrap_err().to_string();
602
603 let expected = r#"
604 error in the following rib found at line 3, column 76
605 `"1"`
606 cause: type mismatch. expected s32, found string
607 the expression `"1"` is inferred as `string` by default
608 "#;
609
610 assert_eq!(error_msg, test_utils::strip_spaces(expected));
611 }
612
613 #[test]
614 fn test_invalid_function_call8() {
615 let expr = r#"
616 let worker = instance("my-worker");
617 let bar = {a: {ac: 1}};
618 worker.foo(bar)
619 "#;
620
621 let expr = Expr::from_text(expr).unwrap();
622
623 let metadata = test_utils::get_metadata();
624
625 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
626 let error_msg = compiler.compile(expr).unwrap_err().to_string();
627
628 let expected = r#"
629 error in the following rib found at line 3, column 32
630 `1`
631 cause: type mismatch. expected list<s32>, found s32
632 the expression `1` is inferred as `s32` by default
633 "#;
634
635 assert_eq!(error_msg, test_utils::strip_spaces(expected));
636 }
637
638 #[test]
639 fn test_invalid_function_call9() {
640 let expr = r#"
641 let worker = instance("my-worker");
642 let result = worker.foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}});
643 result
644 "#;
645
646 let expr = Expr::from_text(expr).unwrap();
647
648 let metadata = test_utils::get_metadata();
649
650 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
651 let error_msg = compiler.compile(expr).unwrap_err().to_string();
652
653 let expected = r#"
654 error in the following rib found at line 3, column 88
655 `2`
656 cause: type mismatch. expected string, found s32
657 the expression `2` is inferred as `s32` by default
658 "#;
659
660 assert_eq!(error_msg, test_utils::strip_spaces(expected));
661 }
662
663 #[test]
664 fn test_invalid_function_call10() {
665 let expr = r#"
666 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3]});
667 result
668 "#;
669
670 let expr = Expr::from_text(expr).unwrap();
671
672 let metadata = test_utils::get_metadata();
673
674 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
675 let error_msg = compiler.compile(expr).unwrap_err().to_string();
676
677 let expected = r#"
678 error in the following rib found at line 2, column 28
679 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ada: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3]}`
680 cause: invalid argument to the function `foo`. missing field(s) in record: `d`
681 "#;
682
683 assert_eq!(error_msg, test_utils::strip_spaces(expected));
684 }
685
686 #[test]
687 fn test_invalid_function_call11() {
688 let expr = r#"
689 let result = foo({a: {aa: 1, ab: 2, ac: [1, 2], ad: {ad: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}});
690 result
691 "#;
692
693 let expr = Expr::from_text(expr).unwrap();
694
695 let metadata = test_utils::get_metadata();
696
697 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
698 let error_msg = compiler.compile(expr).unwrap_err().to_string();
699
700 let expected = r#"
701 error in the following rib found at line 2, column 28
702 `{a: {aa: 1, ab: 2, ac: [1, 2], ad: {ad: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}}`
703 cause: invalid argument to the function `foo`. missing field(s) in record: `a.ad.ada`
704 "#;
705
706 assert_eq!(error_msg, test_utils::strip_spaces(expected));
707 }
708
709 #[test]
710 fn test_invalid_function_call12() {
711 let expr = r#"
712 let result = foo({aa: {aa: 1, ab: 2, ac: [1, 2], ad: {ad: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}});
713 result
714 "#;
715
716 let expr = Expr::from_text(expr).unwrap();
717
718 let metadata = test_utils::get_metadata();
719
720 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
721 let error_msg = compiler.compile(expr).unwrap_err().to_string();
722
723 let expected = r#"
724 error in the following rib found at line 2, column 28
725 `{aa: {aa: 1, ab: 2, ac: [1, 2], ad: {ad: 1}, ae: (1, 2)}, b: 3, c: [1, 2, 3], d: {da: 4}}`
726 cause: invalid argument to the function `foo`. missing field(s) in record: `a`
727 "#;
728
729 assert_eq!(error_msg, test_utils::strip_spaces(expected));
730 }
731
732 #[test]
733 fn test_invalid_function_call13() {
734 let expr = r#"
735 let aa = 1;
736 let result = foo({aa: 1});
737 result
738 "#;
739
740 let expr = Expr::from_text(expr).unwrap();
741
742 let metadata = test_utils::get_metadata();
743
744 let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![]));
745 let error_msg = compiler.compile(expr).unwrap_err().to_string();
746
747 let expected = r#"
748 error in the following rib found at line 3, column 28
749 `{aa: 1}`
750 cause: invalid argument to the function `foo`. missing field(s) in record: `a, b, c, d`
751 "#;
752
753 assert_eq!(error_msg, test_utils::strip_spaces(expected));
754 }
755
756 #[test]
757 async fn test_invalid_resource_constructor_call0() {
758 let expr = r#"
759 let worker = instance("my-worker");
760 let x = worker.cart()
761 "#;
762 let expr = Expr::from_text(expr).unwrap();
763 let component_metadata = test_utils::get_metadata();
764
765 let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]);
766 let compiler = RibCompiler::new(compiler_config);
767 let error_message = compiler.compile(expr).unwrap_err().to_string();
768
769 let expected = r#"
770 error in the following rib found at line 3, column 19
771 `worker.cart()`
772 cause: invalid argument size for function `cart`. expected 1 arguments, found 0
773 "#;
774
775 assert_eq!(error_message, strip_spaces(expected));
776 }
777
778 #[test]
779 async fn test_invalid_resource_constructor_call1() {
780 let expr = r#"
781 let worker = instance("my-worker");
782 let x = worker.cart(1)
783 "#;
784 let expr = Expr::from_text(expr).unwrap();
785 let component_metadata = test_utils::get_metadata();
786
787 let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]);
788 let compiler = RibCompiler::new(compiler_config);
789 let error_message = compiler.compile(expr).unwrap_err().to_string();
790
791 let expected = r#"
792 error in the following rib found at line 3, column 31
793 `1`
794 cause: type mismatch. expected string, found s32
795 invalid argument to the function `cart`
796 "#;
797
798 assert_eq!(error_message, strip_spaces(expected));
799 }
800
801 #[test]
802 async fn test_invalid_resource_method_call0() {
803 let expr = r#"
804 let worker = instance("my-worker");
805 let x = worker.cart("foo");
806 x.add-item(1)
807 "#;
808 let expr = Expr::from_text(expr).unwrap();
809 let component_metadata = test_utils::get_metadata();
810
811 let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]);
812 let compiler = RibCompiler::new(compiler_config);
813 let error_message = compiler.compile(expr).unwrap_err().to_string();
814
815 let expected = r#"
816 error in the following rib found at line 4, column 22
817 `1`
818 cause: type mismatch. expected record { product-id: string, name: string, price: f32, quantity: u32 }, found s32
819 invalid argument to the function `add-item`
820 "#;
821
822 assert_eq!(error_message, strip_spaces(expected));
823 }
824 }
825
826 mod test_utils {
827 use crate::wit_type::{
828 case, f32, field, handle, list, record, s32, str, tuple, u32, u64, variant,
829 };
830 use crate::wit_type::{
831 AnalysedResourceId, AnalysedResourceMode, NameTypePair, WitExport, WitFunction,
832 WitFunctionParameter, WitFunctionResult, WitInterface,
833 };
834 use crate::{ComponentDependency, ComponentDependencyKey};
835 use uuid::Uuid;
836
837 pub(crate) fn strip_spaces(input: &str) -> String {
838 let lines = input.lines();
839
840 let first_line = lines
841 .clone()
842 .find(|line| !line.trim().is_empty())
843 .unwrap_or("");
844 let margin_width = first_line.chars().take_while(|c| c.is_whitespace()).count();
845
846 let result = lines
847 .map(|line| {
848 if line.trim().is_empty() {
849 String::new()
850 } else {
851 line[margin_width..].to_string()
852 }
853 })
854 .collect::<Vec<String>>()
855 .join("\n");
856
857 result.strip_prefix("\n").unwrap_or(&result).to_string()
858 }
859
860 pub(crate) fn get_metadata() -> ComponentDependency {
861 let function_export = WitExport::Function(WitFunction {
862 name: "foo".to_string(),
863 parameters: vec![WitFunctionParameter {
864 name: "arg1".to_string(),
865 typ: record(vec![
866 NameTypePair {
867 name: "a".to_string(),
868 typ: record(vec![
869 NameTypePair {
870 name: "aa".to_string(),
871 typ: s32(),
872 },
873 NameTypePair {
874 name: "ab".to_string(),
875 typ: s32(),
876 },
877 NameTypePair {
878 name: "ac".to_string(),
879 typ: list(s32()),
880 },
881 NameTypePair {
882 name: "ad".to_string(),
883 typ: record(vec![NameTypePair {
884 name: "ada".to_string(),
885 typ: s32(),
886 }]),
887 },
888 NameTypePair {
889 name: "ae".to_string(),
890 typ: tuple(vec![s32(), str()]),
891 },
892 ]),
893 },
894 NameTypePair {
895 name: "b".to_string(),
896 typ: u64(),
897 },
898 NameTypePair {
899 name: "c".to_string(),
900 typ: list(s32()),
901 },
902 NameTypePair {
903 name: "d".to_string(),
904 typ: record(vec![NameTypePair {
905 name: "da".to_string(),
906 typ: s32(),
907 }]),
908 },
909 ]),
910 }],
911 result: Some(WitFunctionResult { typ: str() }),
912 });
913
914 let resource_export = WitExport::Interface(WitInterface {
915 name: "golem:it/api".to_string(),
916 functions: vec![
917 WitFunction {
918 name: "[constructor]cart".to_string(),
919 parameters: vec![WitFunctionParameter {
920 name: "cons".to_string(),
921 typ: str(),
922 }],
923 result: Some(WitFunctionResult {
924 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Owned),
925 }),
926 },
927 WitFunction {
928 name: "[method]cart.add-item".to_string(),
929 parameters: vec![
930 WitFunctionParameter {
931 name: "self".to_string(),
932 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
933 },
934 WitFunctionParameter {
935 name: "item".to_string(),
936 typ: record(vec![
937 field("product-id", str()),
938 field("name", str()),
939 field("price", f32()),
940 field("quantity", u32()),
941 ]),
942 },
943 ],
944 result: None,
945 },
946 WitFunction {
947 name: "[method]cart.remove-item".to_string(),
948 parameters: vec![
949 WitFunctionParameter {
950 name: "self".to_string(),
951 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
952 },
953 WitFunctionParameter {
954 name: "product-id".to_string(),
955 typ: str(),
956 },
957 ],
958 result: None,
959 },
960 WitFunction {
961 name: "[method]cart.update-item-quantity".to_string(),
962 parameters: vec![
963 WitFunctionParameter {
964 name: "self".to_string(),
965 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
966 },
967 WitFunctionParameter {
968 name: "product-id".to_string(),
969 typ: str(),
970 },
971 WitFunctionParameter {
972 name: "quantity".to_string(),
973 typ: u32(),
974 },
975 ],
976 result: None,
977 },
978 WitFunction {
979 name: "[method]cart.checkout".to_string(),
980 parameters: vec![WitFunctionParameter {
981 name: "self".to_string(),
982 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
983 }],
984 result: Some(WitFunctionResult {
985 typ: variant(vec![
986 case("error", str()),
987 case("success", record(vec![field("order-id", str())])),
988 ]),
989 }),
990 },
991 WitFunction {
992 name: "[method]cart.get-cart-contents".to_string(),
993 parameters: vec![WitFunctionParameter {
994 name: "self".to_string(),
995 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
996 }],
997 result: Some(WitFunctionResult {
998 typ: list(record(vec![
999 field("product-id", str()),
1000 field("name", str()),
1001 field("price", f32()),
1002 field("quantity", u32()),
1003 ])),
1004 }),
1005 },
1006 WitFunction {
1007 name: "[method]cart.merge-with".to_string(),
1008 parameters: vec![
1009 WitFunctionParameter {
1010 name: "self".to_string(),
1011 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
1012 },
1013 WitFunctionParameter {
1014 name: "other-cart".to_string(),
1015 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
1016 },
1017 ],
1018 result: None,
1019 },
1020 WitFunction {
1021 name: "[drop]cart".to_string(),
1022 parameters: vec![WitFunctionParameter {
1023 name: "self".to_string(),
1024 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Owned),
1025 }],
1026 result: None,
1027 },
1028 ],
1029 });
1030
1031 let key = ComponentDependencyKey {
1032 component_name: "some_name".to_string(),
1033 component_id: Uuid::new_v4(),
1034 component_revision: 0,
1035 root_package_name: None,
1036 root_package_version: None,
1037 };
1038
1039 let exports = vec![function_export, resource_export];
1040 ComponentDependency::from_wit_metadata(key, &exports).unwrap()
1041 }
1042 }
1043}