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