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