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