diff --git a/docs/language-guide.md b/docs/language-guide.md index 19e2bed..b93a752 100644 --- a/docs/language-guide.md +++ b/docs/language-guide.md @@ -147,6 +147,27 @@ Exact lowering still depends on your embedder; `example.wit` is the reference. A --- +## 1.1 Environment variables (`env.`) + +Rib supports host input only via **environment variables** with the `env` namespace. + +- Use exactly one segment: `env.` (for example `env.TOKEN_ID`). +- Nested paths are rejected: `env.a.b` is invalid. +- The value is always inferred as `string`. +- Name matching is **exact and 1:1** with the environment key. + - If the key is `TOKEN_ID`, use `env.TOKEN_ID`. + - If the key is `TOKEN-ID`, use `env.TOKEN-ID`. + - If the key is `token`, use `env.token`. +- There is no normalization or aliasing (`TOKEN_ID` is not auto-mapped from `token-id`, etc.). + +```rust +let token: string = env.TOKEN_ID; +let region: string = env.DEPLOY_REGION; +"token=${token}, region=${region}" +``` + +--- + ## 2. Programs, blocks, and semicolons A Rib **program** is a sequence of expressions separated by **`;`**. The value of the whole program is the **last** expression (REPLs usually print that). diff --git a/rib-lang/README.md b/rib-lang/README.md index b56e081..a546074 100644 --- a/rib-lang/README.md +++ b/rib-lang/README.md @@ -56,6 +56,24 @@ The **`rib-repl`** crate in this repository consumes the same pipeline for inter --- +## Environment input model + +Rib reads host-provided input only via `env.`. + +- **Single segment only**: `env.a` is valid; `env.a.b` is rejected. +- **Exact key mapping**: `` is used as-is for lookup in `RibInput` and process environment variables. +- **String-only**: values from `env.` are always typed as `string`. + +Examples: + +- env key `TOKEN_ID` -> `env.TOKEN_ID` +- env key `TOKEN-ID` -> `env.TOKEN-ID` +- env key `token` -> `env.token` + +No key normalization or alternate spellings are applied. + +--- + ## Advanced usage (beyond the REPL) Most people meet Rib in a **REPL**; **`rib-lang`** is also for **embedding** in your own Rust binary. There, Rib helps when components grow **many exports**, when you **revise WIT or ship new component versions often**, or when you want **short, typed programs** plugged into the **output** of component calls—post-process, reshape, validate—without hand-writing and re-hand-writing the same glue in Rust (or JSON shims) for every export and every shape change. You wire **analysed exports** into the registry once; Rib text is **checked against that surface** on each compile, so updating the component tends to surface mistakes in the script **before** a bad call reaches Wasm. diff --git a/rib-lang/regression_tests/lib.rs b/rib-lang/regression_tests/lib.rs index 401dedc..be46911 100644 --- a/rib-lang/regression_tests/lib.rs +++ b/rib-lang/regression_tests/lib.rs @@ -14,9 +14,9 @@ use rib::{ async fn rib_compiler_interpreter_end_to_end_worker_metadata_matrix() { let expr = r#" let worker = instance(); - let str1: string = request.body.name; - let str2: string = request.headers.name; - let str3: string = request.path.name; + let str1: string = env.API_TOKEN; + let str2: string = env.CLIENT_NAME; + let str3: string = env.DEPLOY_REGION; let unused = worker.function-unit-response(str1); @@ -474,7 +474,6 @@ async fn rib_compiler_interpreter_end_to_end_worker_metadata_matrix() { let compiler = RibCompiler::new(RibCompilerConfig::new( component_metadata::component_metadata(), vec![], - vec![], )); use std::time::Instant; @@ -1709,7 +1708,7 @@ mod mock_data { } mod mock_interpreter { - use crate::{mock_data, test_utils, Interpreter}; + use crate::{mock_data, Interpreter}; use crate::{ EvaluatedFnArgs, EvaluatedFqFn, EvaluatedWorkerName, RibComponentFunctionInvoke, RibFunctionInvokeResult, RibInput, @@ -1717,7 +1716,6 @@ mod mock_interpreter { use async_trait::async_trait; use rib::wit_type::WitType; - use rib::wit_type::{field, record, str}; use rib::ValueAndType; use rib::{ComponentDependencyKey, DefaultWorkerNameGenerator, InstructionId}; use std::collections::HashMap; @@ -1876,19 +1874,19 @@ mod mock_interpreter { .map(|(name, result)| (FunctionName(name.to_string()), result)) .collect(); - let record_input_type = record(vec![ - field("headers", record(vec![field("name", str())])), - field("body", record(vec![field("name", str())])), - field("path", record(vec![field("name", str())])), - ]); - - let record_input_value = test_utils::get_value_and_type( - &record_input_type, - r#" { headers : { name : "foo" }, body : { name : "bar" }, path : { name : "baz" } }"#, - ); - let mut interpreter_env_input: HashMap = HashMap::new(); - interpreter_env_input.insert("request".to_string(), record_input_value); + interpreter_env_input.insert( + "API_TOKEN".to_string(), + ValueAndType::new(rib::Value::String("bar".to_string()), rib::wit_type::str()), + ); + interpreter_env_input.insert( + "CLIENT_NAME".to_string(), + ValueAndType::new(rib::Value::String("foo".to_string()), rib::wit_type::str()), + ); + interpreter_env_input.insert( + "DEPLOY_REGION".to_string(), + ValueAndType::new(rib::Value::String("baz".to_string()), rib::wit_type::str()), + ); dynamic_test_interpreter(functions_and_result, interpreter_env_input) } diff --git a/rib-lang/src/compiler/byte_code.rs b/rib-lang/src/compiler/byte_code.rs index 296862f..2a04f9d 100644 --- a/rib-lang/src/compiler/byte_code.rs +++ b/rib-lang/src/compiler/byte_code.rs @@ -324,8 +324,17 @@ mod internal { } Expr::SelectField { expr, field, .. } => { - stack.push(ExprState::from_expr(expr.deref())); - instructions.push(RibIR::SelectField(field.clone())); + if let Expr::Identifier { variable_id, .. } = expr.as_ref() { + if variable_id.is_global() && variable_id.name() == "env" { + instructions.push(RibIR::LoadEnvVar(field.clone())); + } else { + stack.push(ExprState::from_expr(expr.deref())); + instructions.push(RibIR::SelectField(field.clone())); + } + } else { + stack.push(ExprState::from_expr(expr.deref())); + instructions.push(RibIR::SelectField(field.clone())); + } } Expr::SelectIndex { expr, index, .. } => match index.inferred_type().internal_type() { @@ -1417,7 +1426,7 @@ mod compiler_tests { #[test] fn test_unknown_function() { let expr = r#" - foo(request); + foo(env.x); "success" "#; @@ -1426,7 +1435,7 @@ mod compiler_tests { let compiler_error = compiler.compile(expr).unwrap_err().to_string(); - assert_eq!(compiler_error, "error in the following rib found at line 2, column 16\n`foo(request)`\ncause: invalid function call `foo`\nunknown function\n"); + assert_eq!(compiler_error, "error in the following rib found at line 2, column 16\n`foo(env.x)`\ncause: invalid function call `foo`\nunknown function\n"); } #[test] @@ -1441,7 +1450,7 @@ mod compiler_tests { let expr = Expr::from_text(expr).unwrap(); - let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(metadata, vec![]); let compiler = RibCompiler::new(compiler_config); @@ -1463,7 +1472,7 @@ mod compiler_tests { let expr = Expr::from_text(expr).unwrap(); - let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(metadata, vec![]); let compiler = RibCompiler::new(compiler_config); @@ -1486,7 +1495,7 @@ mod compiler_tests { let expr = Expr::from_text(expr).unwrap(); - let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(metadata, vec![]); let compiler = RibCompiler::new(compiler_config); @@ -1498,353 +1507,10 @@ mod compiler_tests { } } - #[cfg(test)] - mod global_input_tests { - use test_r::test; - - use crate::compiler::byte_code::compiler_tests::internal; - use crate::wit_type::{ - case, field, list, option, r#enum, record, result, str, tuple, u32, u64, unit_case, - variant, - }; - use crate::{Expr, RibCompiler, RibCompilerConfig}; - - #[test] - async fn test_str_global_input() { - let request_value_type = str(); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - let expr = r#" - let x = request; - let worker = instance(); - worker.my-worker-function(x); - match x { - "foo" => "success", - _ => "fallback" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_number_global_input() { - let request_value_type = u32(); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - let expr = r#" - let x = request; - let worker = instance(); - worker.my-worker-function(x); - match x { - 1 => "success", - 0 => "failure" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_variant_type_info() { - let request_value_type = variant(vec![ - case("register-user", u64()), - case("process-user", str()), - unit_case("validate"), - ]); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Variant as input, - // implies the type of request is a Variant. - // This means the rib interpreter env has to have a request variable in it, - // with a value that should be of the type Variant - let expr = r#" - let worker = instance(); - worker.my-worker-function(request); - match request { - process-user(user) => user, - _ => "default" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_result_type_info() { - let request_value_type = result(u64(), str()); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Result as input, - // implies the type of request is a Result. - // This means the rib interpreter env has to have a request variable in it, - // with a value that should be of the type Result - let expr = r#" - let worker = instance(); - worker.my-worker-function(request); - match request { - ok(x) => "${x}", - err(msg) => msg - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_option_type_info() { - let request_value_type = option(str()); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Option as input, - // implies the type of request is a Result. - // This means the rib interpreter env has to have a request variable in it, - // with a value that should be of the type Option - let expr = r#" - let worker = instance(); - worker.my-worker-function(request); - match request { - some(x) => x, - none => "error" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_enum_type_info() { - let request_value_type = r#enum(&["prod", "dev", "test"]); - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Option as input, - // implies the type of request is a Result. - // This means the rib interpreter env has to have a request variable in it, - // with a value that should be of the type Option - let expr = r#" - let worker = instance(); - worker.my-worker-function(request); - match request { - prod => "p", - dev => "d", - test => "t" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_record_global_input() { - let request_value_type = - record(vec![field("path", record(vec![field("user", str())]))]); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Record of path -> user -> str as input - // implies the type of request is a Record. - // This means the rib interpreter env has to have a request variable in it, - // with a value that should be of the type Record - let expr = r#" - let x = request; - let worker = instance(); - worker.my-worker-function(x); - - let name = x.path.user; - - match x { - { path : { user : some_name } } => some_name, - _ => name - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_tuple_global_input() { - let request_value_type = tuple(vec![str(), u32(), record(vec![field("user", str())])]); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a Tuple, - // implies the type of request is a Tuple. - let expr = r#" - let x = request; - let worker = instance(); - worker.my-worker-function(x); - match x { - (_, _, record) => record.user, - _ => "fallback" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - - #[test] - async fn test_list_global_input() { - let request_value_type = list(str()); - - let output_analysed_type = str(); - - let analysed_exports = internal::get_component_metadata( - "my-worker-function", - vec![request_value_type.clone()], - output_analysed_type, - ); - - // x = request, implies we are expecting a global variable - // called request as the input to Rib. - // my-worker-function is a function that takes a List, - // implies the type of request should be a List - let expr = r#" - let x = request; - let worker = instance(); - worker.my-worker-function(x); - match x { - [a, b, c] => a, - _ => "fallback" - } - "#; - - let expr = Expr::from_text(expr).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); - let compiled = compiler.compile(expr).unwrap(); - let expected_type_info = - internal::rib_input_type_info(vec![("request", request_value_type)]); - - assert_eq!(compiled.rib_input_type_info, expected_type_info); - } - } - mod internal { use crate::wit_type::*; use crate::wit_type::{case, str, u64, unit_case, variant}; - use crate::{ComponentDependency, ComponentDependencyKey, RibInputTypeInfo}; - use std::collections::HashMap; + use crate::{ComponentDependency, ComponentDependencyKey}; use uuid::Uuid; pub(crate) fn metadata_with_variants() -> ComponentDependency { @@ -1911,13 +1577,5 @@ mod compiler_tests { })]; ComponentDependency::from_wit_metadata(component_info, &exports).unwrap() } - - pub(crate) fn rib_input_type_info(types: Vec<(&str, WitType)>) -> RibInputTypeInfo { - let mut type_info = HashMap::new(); - for (name, typ) in types { - type_info.insert(name.to_string(), typ); - } - RibInputTypeInfo { types: type_info } - } } } diff --git a/rib-lang/src/compiler/compiler_output.rs b/rib-lang/src/compiler/compiler_output.rs index 8fd7f43..4a2a9fe 100644 --- a/rib-lang/src/compiler/compiler_output.rs +++ b/rib-lang/src/compiler/compiler_output.rs @@ -1,9 +1,9 @@ -use crate::compiler::worker_functions_in_rib::WorkerFunctionsInRib; +use crate::compiler::worker_functions_in_rib::SideEffectFunctions; use crate::{RibByteCode, RibInputTypeInfo, RibOutputTypeInfo}; #[derive(Debug, Clone)] pub struct CompilerOutput { - pub worker_invoke_calls: Option, + pub worker_invoke_calls: Option, pub byte_code: RibByteCode, pub rib_input_type_info: RibInputTypeInfo, // Optional to keep backward compatible as compiler output information diff --git a/rib-lang/src/compiler/desugar.rs b/rib-lang/src/compiler/desugar.rs index a14c441..e36e397 100644 --- a/rib-lang/src/compiler/desugar.rs +++ b/rib-lang/src/compiler/desugar.rs @@ -659,7 +659,6 @@ mod desugar_tests { RibCompiler::new(RibCompilerConfig::new( ComponentDependency::from_wit_metadata(component_dependency_key, &metadata).unwrap(), vec![], - vec![], )) } diff --git a/rib-lang/src/compiler/ir.rs b/rib-lang/src/compiler/ir.rs index 8ec0a71..297d91b 100644 --- a/rib-lang/src/compiler/ir.rs +++ b/rib-lang/src/compiler/ir.rs @@ -9,6 +9,8 @@ pub enum RibIR { PushLit(ValueAndType), AssignVar(VariableId), LoadVar(VariableId), + /// One segment after `env.` only (`env.a`); nested `env.a.b` is rejected at type-check. Field maps to env keys (e.g. `TOKEN_ID`). + LoadEnvVar(String), CreateAndPushRecord(WitType), UpdateRecord(String), PushList(WitType, usize), diff --git a/rib-lang/src/compiler/mod.rs b/rib-lang/src/compiler/mod.rs index 9c9ca31..7719d4f 100644 --- a/rib-lang/src/compiler/mod.rs +++ b/rib-lang/src/compiler/mod.rs @@ -7,8 +7,8 @@ pub use worker_functions_in_rib::*; use crate::rib_type_error::RibTypeError; use crate::wit_type::{TypeEnum, TypeVariant}; use crate::{ - ComponentDependency, CustomInstanceSpec, Expr, GlobalVariableTypeSpec, InferredExpr, - RibInputTypeInfo, RibOutputTypeInfo, + ComponentDependency, CustomInstanceSpec, Expr, InferredExpr, RibInputTypeInfo, + RibOutputTypeInfo, }; use std::error::Error; use std::fmt::Display; @@ -23,17 +23,13 @@ mod worker_functions_in_rib; #[derive(Default)] pub struct RibCompiler { component: ComponentDependency, - global_variable_type_spec: Vec, custom_instance_spec: Vec, } impl RibCompiler { pub fn new(config: RibCompilerConfig) -> RibCompiler { - let global_variable_type_spec = config.input_spec; - RibCompiler { component: config.component, - global_variable_type_spec, custom_instance_spec: config.custom_instance_spec, } } @@ -47,12 +43,7 @@ impl RibCompiler { let _p = crate::profile::Scope::new( "compile: RibCompiler.infer_types InferredExpr::from_expr", ); - InferredExpr::from_expr( - expr_for_infer, - &self.component, - &self.global_variable_type_spec, - &self.custom_instance_spec, - ) + InferredExpr::from_expr(expr_for_infer, &self.component, &self.custom_instance_spec) }; result.map_err(|err| { let rib_type_error = RibTypeError::from_rib_type_error_internal(err, expr); @@ -80,7 +71,7 @@ impl RibCompiler { let function_calls_identified = { let _p = crate::profile::Scope::new("compile: identity function call"); - WorkerFunctionsInRib::from_inferred_expr(&inferred_expr, &self.component)? + SideEffectFunctions::from_inferred_expr(&inferred_expr, &self.component)? }; let global_input_type_info = { @@ -92,30 +83,6 @@ impl RibCompiler { RibOutputTypeInfo::from_expr(&inferred_expr)? }; - // allowed_global_variables - let allowed_global_variables: Vec = self - .global_variable_type_spec - .iter() - .map(|x| x.variable()) - .collect::>(); - - let mut unidentified_global_inputs = vec![]; - - if !allowed_global_variables.is_empty() { - for (name, _) in global_input_type_info.types.iter() { - if !allowed_global_variables.contains(name) { - unidentified_global_inputs.push(name.clone()); - } - } - } - - if !unidentified_global_inputs.is_empty() { - return Err(RibCompilationError::UnsupportedGlobalInput { - invalid_global_inputs: unidentified_global_inputs, - valid_global_inputs: allowed_global_variables, - }); - } - let byte_code = { let _p = crate::profile::Scope::new("compile: byte code generation"); RibByteCode::from_expr(&inferred_expr)? @@ -138,36 +105,21 @@ impl RibCompiler { } } -/// Compiler configuration options for Rib. -/// -/// # Fields -/// - `component_metadata`: Component metadata that describes the worker functions available. -/// - `global_input_spec`: Defines constraints and types for global input variables. -/// By default, Rib allows any identifier (e.g., `foo`) to be treated as a global variable. -/// A global variable is a variable that is not defined in the Rib script but is expected to be provided -/// by the environment in which the Rib script is executed (e.g., `request`, `env`). Hence it is called `global_input`. -/// This field can restrict global variables to a predefined set. If the field is empty, any identifier -/// can be used as a global variable. -/// -/// You can also associate specific types with known global variables using -/// `GlobalVariableTypeSpec`. For example, the path `request.path.*` can be enforced to always -/// be of type `string`. Note that not all global variables require a type specification. +/// Compiler configuration: component exports and optional custom instance factories. +/// Scripts read inputs only via `env.`; `` is the exact environment variable key (string at run time). #[derive(Default)] pub struct RibCompilerConfig { pub component: ComponentDependency, - input_spec: Vec, - custom_instance_spec: Vec, + pub custom_instance_spec: Vec, } impl RibCompilerConfig { pub fn new( component: ComponentDependency, - input_spec: Vec, custom_instance_spec: Vec, - ) -> RibCompilerConfig { + ) -> Self { RibCompilerConfig { component, - input_spec, custom_instance_spec, } } @@ -200,20 +152,6 @@ pub enum RibCompilationError { // This captures only the syntax parse errors in a Rib script. InvalidSyntax(String), - // This occurs when the Rib script includes global inputs that cannot be - // fulfilled. For example, if Rib is used from a REPL, the only valid global input will be `env`. - // If it is used from the Golem API gateway, it is `request`. - // If the user specifies a global input such as `foo` - // (e.g., the compiler will treat `foo` as a global input in a Rib script like `my-worker-function(foo)`), - // it will fail compilation with this error. - // Note: the type inference phase will still be happy with this Rib script; - // we perform this validation as an extra step at the end to allow clients of `golem-rib` - // to decide what global inputs are valid. - UnsupportedGlobalInput { - invalid_global_inputs: Vec, - valid_global_inputs: Vec, - }, - // A typical use of static analysis in Rib is to identify all the valid worker functions. // If this analysis phase fails, it typically indicates a bug in the Rib compiler. RibStaticAnalysisError(String), @@ -239,17 +177,6 @@ impl Display for RibCompilationError { } RibCompilationError::RibTypeError(err) => write!(f, "{err}"), RibCompilationError::InvalidSyntax(msg) => write!(f, "invalid rib syntax: {msg}"), - RibCompilationError::UnsupportedGlobalInput { - invalid_global_inputs, - valid_global_inputs, - } => { - write!( - f, - "unsupported global input variables: {}. expected: {}", - invalid_global_inputs.join(", "), - valid_global_inputs.join(", ") - ) - } RibCompilationError::ByteCodeGenerationFail(e) => { write!(f, "{e}") } @@ -282,7 +209,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -309,7 +236,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -336,7 +263,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -364,7 +291,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -395,7 +322,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -421,7 +348,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -446,7 +373,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -471,7 +398,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -496,7 +423,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -524,7 +451,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -548,7 +475,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -572,7 +499,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -597,7 +524,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -622,7 +549,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -647,7 +574,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -671,7 +598,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -694,7 +621,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -717,7 +644,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -741,7 +668,7 @@ mod compiler_error_tests { let metadata = test_utils::get_metadata(); - let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(metadata, vec![])); let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" @@ -762,7 +689,7 @@ mod compiler_error_tests { let expr = Expr::from_text(expr).unwrap(); let component_metadata = test_utils::get_metadata(); - let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(component_metadata, vec![]); let compiler = RibCompiler::new(compiler_config); let error_message = compiler.compile(expr).unwrap_err().to_string(); @@ -784,7 +711,7 @@ mod compiler_error_tests { let expr = Expr::from_text(expr).unwrap(); let component_metadata = test_utils::get_metadata(); - let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(component_metadata, vec![]); let compiler = RibCompiler::new(compiler_config); let error_message = compiler.compile(expr).unwrap_err().to_string(); @@ -808,7 +735,7 @@ mod compiler_error_tests { let expr = Expr::from_text(expr).unwrap(); let component_metadata = test_utils::get_metadata(); - let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(component_metadata, vec![]); let compiler = RibCompiler::new(compiler_config); let error_message = compiler.compile(expr).unwrap_err().to_string(); diff --git a/rib-lang/src/compiler/worker_functions_in_rib.rs b/rib-lang/src/compiler/worker_functions_in_rib.rs index 57c1eb6..97c4b57 100644 --- a/rib-lang/src/compiler/worker_functions_in_rib.rs +++ b/rib-lang/src/compiler/worker_functions_in_rib.rs @@ -9,15 +9,15 @@ use crate::{ComponentDependency, FunctionName, InferredExpr, RibCompilationError // These function calls are indeed worker invoke calls and nothing else. // If Rib has inbuilt function support, those will not be included here either. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct WorkerFunctionsInRib { - pub function_calls: Vec, +pub struct SideEffectFunctions { + pub function_calls: Vec, } -impl WorkerFunctionsInRib { +impl SideEffectFunctions { pub fn from_inferred_expr( inferred_expr: &InferredExpr, component_dependency: &ComponentDependency, - ) -> Result, RibCompilationError> { + ) -> Result, RibCompilationError> { let worker_invoke_registry_keys = inferred_expr.worker_invoke_registry_keys(); let mut function_calls = vec![]; @@ -27,7 +27,7 @@ impl WorkerFunctionsInRib { .get_function_type(&key) .map_err(|e| RibCompilationError::RibStaticAnalysisError(e.to_string()))?; - let function_call_in_rib = WorkerFunctionType { + let function_call_in_rib = SideEffectFunctionSignature { function_name: key, parameter_types: function_type .parameter_types @@ -46,14 +46,14 @@ impl WorkerFunctionsInRib { if function_calls.is_empty() { Ok(None) } else { - Ok(Some(WorkerFunctionsInRib { function_calls })) + Ok(Some(SideEffectFunctions { function_calls })) } } } // The type of a function call with worker (ephmeral or durable) in Rib script #[derive(Debug, Clone, PartialEq, Eq)] -pub struct WorkerFunctionType { +pub struct SideEffectFunctionSignature { pub function_name: FunctionName, pub parameter_types: Vec, pub return_type: Option, diff --git a/rib-lang/src/expr.rs b/rib-lang/src/expr.rs index bf4eac5..b59d9c0 100644 --- a/rib-lang/src/expr.rs +++ b/rib-lang/src/expr.rs @@ -8,8 +8,7 @@ use crate::type_inference as ti; use crate::wit_type::WitType; use crate::{ from_string, text, type_checker, type_inference, ComponentDependency, ComponentDependencyKey, - CustomInstanceSpec, DynamicParsedFunctionName, GlobalVariableTypeSpec, InferredType, - InstanceIdentifier, VariableId, + CustomInstanceSpec, DynamicParsedFunctionName, InferredType, InstanceIdentifier, VariableId, }; use crate::{IntoValueAndType, ValueAndType}; use bigdecimal::{BigDecimal, ToPrimitive}; @@ -1099,7 +1098,6 @@ impl Expr { pub fn infer_types( &mut self, component_dependency: &ComponentDependency, - global_variable_type_spec: &Vec, custom_instance_spec: &[CustomInstanceSpec], ) -> Result<(), RibTypeErrorInternal> { let component = Arc::new(component_dependency.clone()); @@ -1119,7 +1117,6 @@ impl Expr { &mut arena, &mut types, component.clone(), - global_variable_type_spec.as_slice(), custom_instance_spec, )?; @@ -1168,7 +1165,7 @@ impl Expr { ti::push_types_down(root, arena, types)?; ti::infer_all_identifiers(root, arena, types); ti::pull_types_up(root, arena, types, component.as_ref())?; - ti::infer_global_inputs(root, arena, types); + ti::infer_global_inputs(root, arena, types)?; ti::infer_function_call_types( root, arena, diff --git a/rib-lang/src/interpreter/env_resolve.rs b/rib-lang/src/interpreter/env_resolve.rs new file mode 100644 index 0000000..478f106 --- /dev/null +++ b/rib-lang/src/interpreter/env_resolve.rs @@ -0,0 +1,22 @@ +//! Resolve `env.` at run time: the `` segment is the **exact** key used in +//! [`RibInput::input`] and in [`std::env::var`] (no alternate spellings or normalizations). + +use crate::value::Value; +use crate::wit_type::builders::str; +use crate::ValueAndType; + +use super::RibInput; + +/// Read a string: [`RibInput`] overrides (tests / hosts), then the process environment, same key as in Rib. +pub fn resolve_env_string(field: &str, rib_input: &RibInput) -> String { + if let Some(vnt) = rib_input.input.get(field) { + if let Value::String(s) = &vnt.value { + return s.clone(); + } + } + std::env::var(field).unwrap_or_default() +} + +pub fn resolve_env_value_and_type(field: &str, rib_input: &RibInput) -> ValueAndType { + ValueAndType::new(Value::String(resolve_env_string(field, rib_input)), str()) +} diff --git a/rib-lang/src/interpreter/mod.rs b/rib-lang/src/interpreter/mod.rs index 1988cc0..858637f 100644 --- a/rib-lang/src/interpreter/mod.rs +++ b/rib-lang/src/interpreter/mod.rs @@ -1,4 +1,5 @@ pub use env::*; +pub use env_resolve::*; pub use eval::*; pub use interpreter_input::*; pub use interpreter_result::*; @@ -9,6 +10,7 @@ pub use rib_runtime_error::*; pub use stack::*; mod env; +mod env_resolve; mod eval; mod instruction_cursor; mod interpreter_input; diff --git a/rib-lang/src/interpreter/rib_interpreter.rs b/rib-lang/src/interpreter/rib_interpreter.rs index 6b16ba7..9607961 100644 --- a/rib-lang/src/interpreter/rib_interpreter.rs +++ b/rib-lang/src/interpreter/rib_interpreter.rs @@ -178,6 +178,14 @@ impl Interpreter { )?; } + RibIR::LoadEnvVar(field) => { + let v = crate::interpreter::env_resolve::resolve_env_value_and_type( + field.as_str(), + &self.input, + ); + stack.push_val(v); + } + RibIR::IsEmpty => { internal::run_is_empty_instruction(&mut stack)?; } @@ -1516,12 +1524,12 @@ mod tests { }; use crate::wit_type::WitType; use crate::wit_type::{ - bool, case, f32, field, list, option, r#enum, record, result, result_err, result_ok, s32, - str, tuple, u32, u64, u8, unit_case, variant, + bool, case, field, list, option, r#enum, record, result, result_err, result_ok, s32, str, + tuple, u32, u64, u8, unit_case, variant, }; use crate::{ - ComponentDependency, CustomInstanceSpec, Expr, GlobalVariableTypeSpec, InferredType, - InstructionId, Path, RibCompiler, RibCompilerConfig, VariableId, + ComponentDependency, CustomInstanceSpec, Expr, InstructionId, RibCompiler, + RibCompilerConfig, VariableId, }; use crate::{IntoValue, IntoValueAndType, Value, ValueAndType}; @@ -1812,115 +1820,27 @@ mod tests { } #[test] - async fn interpreter_global_variable_paths_respect_type_spec() { - // request.path.user-id and request.headers.* should be inferred as string, - // since we configure the compiler with a type-spec (given below) + async fn interpreter_env_fields_are_strings_from_rib_input() { let rib_expr = r#" - let res1 = request.path.user-id; - let res2 = request.headers.name; - let res3 = request.headers.age; + let res1 = env.USER_ID; + let res2 = env.NAME; + let res3 = env.AGE; "${res1}-${res2}-${res3}" "#; - let type_spec = vec![ - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["path"]), - InferredType::string(), - ), - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["headers"]), - InferredType::string(), - ), - ]; - let mut rib_input = HashMap::new(); - - // Rib compiler identifies the input requirements to be a string (due to type-spec passed) - // and therefore, we pass input value (value_and_type) to the interpreter with headers and path values as string - let analysed_type_of_input = &record(vec![ - field("path", record(vec![field("user-id", str())])), - field( - "headers", - record(vec![field("name", str()), field("age", str())]), - ), - ]); - - let value_and_type = get_value_and_type( - analysed_type_of_input, - r#"{path : { user-id: "1" }, headers: { name: "foo", age: "20" }}"#, + rib_input.insert( + "USER_ID".to_string(), + ValueAndType::new(Value::String("1".to_string()), str()), ); - - rib_input.insert("request".to_string(), value_and_type); - - let mut interpreter = - test_utils::interpreter_with_noop_function_invoke(Some(RibInput::new(rib_input))); - - let expr = Expr::from_text(rib_expr).unwrap(); - - let compiler = RibCompiler::new(RibCompilerConfig::new( - ComponentDependency::default(), - type_spec, - vec![], - )); - let compiled = compiler.compile(expr).unwrap(); - - let result = interpreter - .run(compiled.byte_code) - .await - .unwrap() - .get_val() - .unwrap() - .value; - - assert_eq!(result, Value::String("1-foo-20".to_string())) - } - - #[test] - async fn interpreter_global_variable_type_annotations_override_spec() { - let rib_expr = r#" - let res1: u32 = request.path.user-id; - let res2 = request.headers.name; - let res3: u32 = request.headers.age; - let res4 = res1 + res3; - "${res4}-${res2}" - "#; - - // We always specify the type of request.path.* and request.headers.* to be a string using type-spec - // however the rib script (above) explicitly specify the type of request.path.user-id - // and request.header.age to be u32. In this case, the Rib compiler infer them as u32 and interpreter works with u32. - let type_spec = vec![ - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["path"]), - InferredType::string(), - ), - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["headers"]), - InferredType::string(), - ), - ]; - - let mut rib_input = HashMap::new(); - - // We pass the input value to rib-interpreter with request.path.user-id - // and request.headers.age as u32, since the compiler inferred these input type requirements to be u32. - let analysed_type_of_input = &record(vec![ - field("path", record(vec![field("user-id", u32())])), - field( - "headers", - record(vec![field("name", str()), field("age", u32())]), - ), - ]); - - let value_and_type = get_value_and_type( - analysed_type_of_input, - r#"{path : { user-id: 1 }, headers: { name: "foo", age: 20 }}"#, + rib_input.insert( + "NAME".to_string(), + ValueAndType::new(Value::String("foo".to_string()), str()), + ); + rib_input.insert( + "AGE".to_string(), + ValueAndType::new(Value::String("20".to_string()), str()), ); - - rib_input.insert("request".to_string(), value_and_type); let mut interpreter = test_utils::interpreter_with_noop_function_invoke(Some(RibInput::new(rib_input))); @@ -1929,10 +1849,8 @@ mod tests { let compiler = RibCompiler::new(RibCompilerConfig::new( ComponentDependency::default(), - type_spec, vec![], )); - let compiled = compiler.compile(expr).unwrap(); let result = interpreter @@ -1943,7 +1861,7 @@ mod tests { .unwrap() .value; - assert_eq!(result, Value::String("21-foo".to_string())) + assert_eq!(result, Value::String("1-foo-20".to_string())) } #[test] @@ -1977,25 +1895,20 @@ mod tests { async fn interpreter_with_variant_and_enum() { let test_deps = RibTestDeps::test_deps_with_global_functions(); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let mut interpreter = test_deps.interpreter; - // This has intentionally got conflicting variable names - // variable `x` is same as the enum name `x` - // similarly, variably `validate` is same as the variant name validate + // Enum / variant names coincide with locals; keep locals unrelated names (`e1`, `v1`). let expr = r#" - let x = x; + let e1 = x; let y = x; let a = instance(); - let result1 = a.add-enum(x, y); - let validate = validate; + let result1 = a.add-enum(e1, y); + let v1 = validate; let validate2 = validate; - let result2 = a.add-variant(validate, validate2); + let result2 = a.add-variant(v1, validate2); {res1: result1, res2: result2} "#; @@ -2033,11 +1946,8 @@ mod tests { async fn interpreter_with_conflicting_variable_names() { let test_deps = RibTestDeps::test_deps_with_global_functions(); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let mut interpreter = test_deps.interpreter; @@ -2211,8 +2121,7 @@ mod tests { None, ); let expr = Expr::from_text(rib).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(component_metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(component_metadata, vec![])); let compiled = compiler.compile(expr).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); assert_eq!( @@ -2241,8 +2150,7 @@ mod tests { let component_metadata = test_utils::configurable_metadata("foo", vec![u32()], Some(u64())); let expr = Expr::from_text(rib).unwrap(); - let compiler = - RibCompiler::new(RibCompilerConfig::new(component_metadata, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(component_metadata, vec![])); assert!(compiler.compile(expr).is_err()); } } @@ -2446,7 +2354,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); - let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![])); let compiled = compiler.compile(expr).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); @@ -2477,7 +2385,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); - let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![])); let compiled = compiler.compile(expr).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); @@ -2514,7 +2422,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); - let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![])); let compiled = compiler.compile(expr).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); @@ -2553,7 +2461,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); - let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![])); let compiled = compiler.compile(expr).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); @@ -2574,7 +2482,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let component_metadata = test_utils::get_metadata_with_resource_with_params(); - let compiler_config = RibCompilerConfig::new(component_metadata, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(component_metadata, vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2598,7 +2506,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2628,7 +2536,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2662,7 +2570,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2694,7 +2602,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2721,7 +2629,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2752,7 +2660,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2783,7 +2691,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2811,7 +2719,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2836,7 +2744,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -2864,7 +2772,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); @@ -2895,7 +2803,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3228,7 +3136,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_for_pass_through_function(); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3262,11 +3170,8 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_global_functions(); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr); @@ -3282,11 +3187,8 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr); @@ -3302,11 +3204,8 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr); @@ -3322,7 +3221,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let error = compiler.compile(expr).unwrap_err(); @@ -3341,11 +3240,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr).unwrap_err().to_string(); @@ -3362,11 +3258,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compilation_error = compiler.compile(expr).unwrap_err().to_string(); @@ -3391,7 +3284,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3412,7 +3305,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_for_pass_through_function(); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3441,7 +3334,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3462,7 +3355,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3487,8 +3380,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = - RibCompiler::new(RibCompilerConfig::new(test_deps.component, vec![], vec![])); + let compiler = RibCompiler::new(RibCompilerConfig::new(test_deps.component, vec![])); let compilation_error = compiler.compile(expr).unwrap_err().to_string(); @@ -3508,7 +3400,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component, vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3532,11 +3424,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr).unwrap_err().to_string(); @@ -3561,7 +3450,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3584,7 +3473,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_multiple_interfaces(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3607,11 +3496,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr); @@ -3630,7 +3516,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3652,7 +3538,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component, vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component, vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3674,11 +3560,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr).unwrap_err().to_string(); @@ -3696,11 +3579,8 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let compiled = compiler.compile(expr).unwrap_err().to_string(); @@ -3722,7 +3602,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3746,11 +3626,8 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler = RibCompiler::new(RibCompilerConfig::new( - test_deps.component.clone(), - vec![], - vec![], - )); + let compiler = + RibCompiler::new(RibCompilerConfig::new(test_deps.component.clone(), vec![])); let error_message = compiler.compile(expr).unwrap_err().to_string(); @@ -3775,7 +3652,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3800,7 +3677,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3829,7 +3706,7 @@ mod tests { "#; let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3868,7 +3745,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler.compile(expr).unwrap(); @@ -3914,7 +3791,7 @@ mod tests { let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(Some(rib_input)); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); @@ -3939,7 +3816,7 @@ mod tests { #[test] async fn interpreter_durable_worker_with_resource_17() { let expr = r#" - let x: string = request.path.user-id; + let x: string = env.USER_ID; let min: u8 = 1; let max: u8 = 3; let result = for i in min..=max { @@ -3953,20 +3830,15 @@ mod tests { let mut input = HashMap::new(); - let rib_input_key = "request"; + let rib_input_value = ValueAndType::new(Value::String("user".to_string()), str()); - let rib_input_value = ValueAndType::new( - Value::Record(vec![Value::Record(vec![Value::String("user".to_string())])]), - record(vec![field("path", record(vec![field("user-id", str())]))]), - ); - - input.insert(rib_input_key.to_string(), rib_input_value); + input.insert("USER_ID".to_string(), rib_input_value); let rib_input = RibInput::new(input); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(Some(rib_input)); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); @@ -4005,42 +3877,18 @@ mod tests { let cart = worker.cart("bar"); for i in range { - yield cart.add-item(request.body); + yield cart.add-item({name: "mac-book", product-id: "mac", quantity: 1:u32, price: 1:f32}); }; "success" "#; let expr = Expr::from_text(expr).unwrap(); - let mut input = HashMap::new(); - - let rib_input_key = "request"; - - let rib_input_value = ValueAndType::new( - Value::Record(vec![Value::Record(vec![ - Value::String("mac-book".to_string()), - Value::String("mac".to_string()), - Value::U32(1), - Value::F32(1.0), - ])]), - record(vec![field( - "body", - record(vec![ - field("name", str()), - field("product-id", str()), - field("quantity", u32()), - field("price", f32()), - ]), - )]), - ); - - input.insert(rib_input_key.to_string(), rib_input_value); - - let rib_input = RibInput::new(input); + let rib_input = RibInput::new(HashMap::new()); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(Some(rib_input)); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); @@ -4064,42 +3912,18 @@ mod tests { for i in range { let worker = instance("my-worker"); let cart = worker.cart("bar"); - yield cart.add-item(request.body); + yield cart.add-item({name: "mac-book", product-id: "mac", quantity: 1:u32, price: 1:f32}); }; "success" "#; let expr = Expr::from_text(expr).unwrap(); - let mut input = HashMap::new(); - - let rib_input_key = "request"; - - let rib_input_value = ValueAndType::new( - Value::Record(vec![Value::Record(vec![ - Value::String("mac-book".to_string()), - Value::String("mac".to_string()), - Value::U32(1), - Value::F32(1.0), - ])]), - record(vec![field( - "body", - record(vec![ - field("name", str()), - field("product-id", str()), - field("quantity", u32()), - field("price", f32()), - ]), - )]), - ); - - input.insert(rib_input_key.to_string(), rib_input_value); - - let rib_input = RibInput::new(input); + let rib_input = RibInput::new(HashMap::new()); let test_deps = RibTestDeps::test_deps_with_indexed_resource_functions(Some(rib_input)); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); @@ -4167,7 +3991,6 @@ mod tests { let compiler_config = RibCompilerConfig::new( test_deps.component.clone(), - vec![], vec![custom_spec1, custom_spec2], ); let compiler = RibCompiler::new(compiler_config); @@ -4216,7 +4039,7 @@ mod tests { let expr = Expr::from_text(expr).unwrap(); let test_deps = RibTestDeps::test_deps_with_variant_conflicts(None); - let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![], vec![]); + let compiler_config = RibCompilerConfig::new(test_deps.component.clone(), vec![]); let compiler = RibCompiler::new(compiler_config); let compiled = compiler .compile(expr) diff --git a/rib-lang/src/type_checker/exhaustive_pattern_match.rs b/rib-lang/src/type_checker/exhaustive_pattern_match.rs index b43c1d0..b1e1f48 100644 --- a/rib-lang/src/type_checker/exhaustive_pattern_match.rs +++ b/rib-lang/src/type_checker/exhaustive_pattern_match.rs @@ -625,6 +625,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_option_pattern_match_wild_card1() { let expr = r#" + let a = "bar"; let x = some("foo"); match x { some(_) => a, @@ -640,6 +641,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_option_pattern_match_wild_card2() { let expr = r#" + let a = "bar"; let x = some("foo"); match x { none => "none", @@ -689,6 +691,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_option_pattern_match_wild_card5() { let expr = r#" + let a = "bar"; let x = some("foo"); match x { some(_) => a, @@ -705,6 +708,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_option_pattern_match_wild_card_invalid1() { let expr = r#" + let a = "z"; let x = some("foo"); match x { _ => "none", @@ -718,7 +722,7 @@ mod pattern_match_exhaustive_tests { let error_msg = compiler.compile(expr).unwrap_err().to_string(); let expected = r#" - error in the following rib found at line 3, column 9 + error in the following rib found at line 4, column 9 `match x { _ => "none", some(_) => a } ` cause: dead code detected, pattern `some(_)` is unreachable due to the existence of the pattern `_` prior to it help: to ensure a complete match, add missing patterns or use wildcard (`_`) @@ -758,7 +762,7 @@ mod pattern_match_exhaustive_tests { let x = some("foo"); match x { something => "none", - some(_) => a + some(_) => "x" } "#; @@ -769,7 +773,7 @@ mod pattern_match_exhaustive_tests { let expected = r#" error in the following rib found at line 3, column 9 - `match x { something => "none", some(_) => a } ` + `match x { something => "none", some(_) => "x" } ` cause: dead code detected, pattern `some(_)` is unreachable due to the existence of the pattern `something` prior to it help: to ensure a complete match, add missing patterns or use wildcard (`_`) "#; @@ -926,6 +930,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_result_pattern_match_wild_card2() { let expr = r#" + let a = "bar"; let x: result = ok("foo"); match x { err(msg) => msg, @@ -975,6 +980,7 @@ mod pattern_match_exhaustive_tests { #[test] fn test_result_pattern_match_wild_card5() { let expr = r#" + let a = "bar"; let x = ok("foo"); match x { ok(_) => a, @@ -994,7 +1000,7 @@ mod pattern_match_exhaustive_tests { let x = ok("foo"); match x { _ => "none", - ok(_) => a + ok(_) => "x" } "#; @@ -1005,7 +1011,7 @@ mod pattern_match_exhaustive_tests { let expected = r#" error in the following rib found at line 3, column 9 - `match x { _ => "none", ok(_) => a } ` + `match x { _ => "none", ok(_) => "x" } ` cause: dead code detected, pattern `ok(_)` is unreachable due to the existence of the pattern `_` prior to it help: to ensure a complete match, add missing patterns or use wildcard (`_`) "#; diff --git a/rib-lang/src/type_checker/unresolved_types.rs b/rib-lang/src/type_checker/unresolved_types.rs index 3443464..028b6e3 100644 --- a/rib-lang/src/type_checker/unresolved_types.rs +++ b/rib-lang/src/type_checker/unresolved_types.rs @@ -39,10 +39,7 @@ mod unresolved_types_tests { let error = r#" error in the following rib found at line 1, column 1 `hello` - cause: cannot determine the type - help: try specifying the expected type explicitly - help: if the issue persists, please review the script for potential type inconsistencies - help: make sure `hello` is a valid identifier + cause: unknown global `hello`. Rib only supports `env.` for inputs (strings from environment variables). "#; assert_eq!(error_msg, strip_spaces(error)); @@ -57,11 +54,7 @@ mod unresolved_types_tests { let expected = r#" error in the following rib found at line 1, column 29 `hello` - cause: cannot determine the type - unresolved type at path: `foo.b[1]` - help: try specifying the expected type explicitly - help: if the issue persists, please review the script for potential type inconsistencies - help: make sure `hello` is a valid identifier + cause: unknown global `hello`. Rib only supports `env.` for inputs (strings from environment variables). "#; assert_eq!(error_msg, strip_spaces(expected)); @@ -76,10 +69,7 @@ mod unresolved_types_tests { let expected = r#" error in the following rib found at line 1, column 4 `hello` - cause: cannot determine the type - help: try specifying the expected type explicitly - help: if the issue persists, please review the script for potential type inconsistencies - help: make sure `hello` is a valid identifier + cause: unknown global `hello`. Rib only supports `env.` for inputs (strings from environment variables). "#; assert_eq!(error_msg, strip_spaces(expected)); @@ -95,10 +85,7 @@ mod unresolved_types_tests { let expected = r#" error in the following rib found at line 1, column 5 `hello` - cause: cannot determine the type - help: try specifying the expected type explicitly - help: if the issue persists, please review the script for potential type inconsistencies - help: make sure `hello` is a valid identifier + cause: unknown global `hello`. Rib only supports `env.` for inputs (strings from environment variables). "#; assert_eq!(error_msg, strip_spaces(expected)); diff --git a/rib-lang/src/type_inference/env_namespace.rs b/rib-lang/src/type_inference/env_namespace.rs new file mode 100644 index 0000000..1959a88 --- /dev/null +++ b/rib-lang/src/type_inference/env_namespace.rs @@ -0,0 +1,121 @@ +//! `env.` is the only supported global input: **exactly one** field after `env` (never `env.a.b`), +//! always [`crate::InferredType::string`]. Nested paths are rejected to keep resolution and typing simple. + +use crate::expr_arena::{ExprArena, ExprId, ExprKind, TypeTable}; +use crate::inferred_type::InferredType; +use crate::rib_type_error::RibTypeErrorInternal; +use crate::type_inference::expr_visitor::arena::children_of; +use crate::CustomError; +use std::collections::HashMap; + +/// Path segments after `env` when `start` is the **outermost** [`ExprKind::SelectField`] (`env.a.b` → `["a","b"]`). +pub fn env_path_from_outer_select(start: ExprId, arena: &ExprArena) -> Option> { + let mut fields = vec![]; + let mut current = start; + loop { + match &arena.expr(current).kind { + ExprKind::SelectField { expr, field } => { + fields.push(field.clone()); + current = *expr; + } + ExprKind::Identifier { variable_id } => { + if variable_id.is_global() && variable_id.name() == "env" { + fields.reverse(); + return Some(fields); + } + return None; + } + _ => return None, + } + } +} + +pub fn env_path_key(path: &[String]) -> String { + path.join(".") +} + +/// Only `env.` (one segment) is typed; inner `env` placeholder is not given a full record type. +pub fn apply_env_namespace_types(root: ExprId, arena: &ExprArena, types: &mut TypeTable) { + let mut stack = vec![root]; + while let Some(id) = stack.pop() { + if let ExprKind::SelectField { expr, .. } = &arena.expr(id).kind { + if let ExprKind::Identifier { variable_id } = &arena.expr(*expr).kind { + if variable_id.is_global() && variable_id.name() == "env" { + types.set(id, InferredType::string()); + } + } + } + for child in children_of(id, arena).into_iter().rev() { + stack.push(child); + } + } +} + +fn build_parent_map(root: ExprId, arena: &ExprArena) -> HashMap { + let mut map = HashMap::new(); + let mut stack = vec![root]; + while let Some(id) = stack.pop() { + for ch in children_of(id, arena) { + map.insert(ch, id); + stack.push(ch); + } + } + map +} + +pub fn validate_global_identifiers( + root: ExprId, + arena: &ExprArena, +) -> Result<(), RibTypeErrorInternal> { + let parent = build_parent_map(root, arena); + let mut stack = vec![root]; + while let Some(id) = stack.pop() { + let span = arena.expr(id).source_span.clone(); + if let ExprKind::SelectField { .. } = &arena.expr(id).kind { + if let Some(path) = env_path_from_outer_select(id, arena) { + if path.len() > 1 { + return Err(CustomError::new( + span, + "only a single field is allowed after `env` (e.g. `env.TOKEN_ID`). nested paths like `env.a.b` are not supported.", + ) + .into()); + } + } + } + if let ExprKind::Identifier { variable_id } = &arena.expr(id).kind { + if variable_id.is_global() { + let name = variable_id.name(); + if name == "env" { + let allowed = parent + .get(&id) + .map(|p| { + matches!( + &arena.expr(*p).kind, + ExprKind::SelectField { expr, .. } if *expr == id + ) + }) + .unwrap_or(false); + if !allowed { + return Err(CustomError::new( + span, + "`env` is reserved: use `env.` (same spelling as the environment variable key, e.g. `env.TOKEN_ID`).", + ) + .into()); + } + } else { + return Err(CustomError::new( + span, + format!( + "unknown global `{name}`. Rib only supports `env.` for inputs (strings from environment variables)." + ), + ) + .into()); + } + } + } + for child in children_of(id, arena).into_iter().rev() { + stack.push(child); + } + } + Ok(()) +} diff --git a/rib-lang/src/type_inference/global_input_inference.rs b/rib-lang/src/type_inference/global_input_inference.rs index 93add73..98fd784 100644 --- a/rib-lang/src/type_inference/global_input_inference.rs +++ b/rib-lang/src/type_inference/global_input_inference.rs @@ -1,55 +1,16 @@ -//! Global-input typing runs on lowered IR; use [`infer_global_inputs`] from the same -//! `lower` / `rebuild_expr` boundary as [`crate::Expr::infer_types`]. +//! After [`crate::type_inference::type_pull_up`], pin `env.*` to [`crate::InferredType::string`] +//! and validate that no other global identifiers appear. -use crate::expr_arena::{ExprArena, ExprId, ExprKind, TypeTable}; -use crate::type_inference::expr_visitor::arena::children_of; -use crate::InferredType; -use std::collections::HashMap; +use crate::expr_arena::{ExprArena, ExprId, TypeTable}; +use crate::rib_type_error::RibTypeErrorInternal; +use crate::type_inference::env_namespace; -pub fn infer_global_inputs(root: ExprId, arena: &ExprArena, types: &mut TypeTable) { - let global_vars = collect_global_variable_types(root, arena, types); - - let mut stack = vec![root]; - while let Some(id) = stack.pop() { - let kind = arena.expr(id).kind.clone(); - if let ExprKind::Identifier { variable_id } = kind { - if variable_id.is_global() { - if let Some(type_list) = global_vars.get(&variable_id.name()) { - types.set(id, InferredType::all_of(type_list.clone())); - } - } - } else { - for child in children_of(id, arena).into_iter().rev() { - stack.push(child); - } - } - } -} - -fn collect_global_variable_types( +pub fn infer_global_inputs( root: ExprId, arena: &ExprArena, - types: &TypeTable, -) -> HashMap> { - let mut all_types: HashMap> = HashMap::new(); - - let mut stack = vec![root]; - while let Some(id) = stack.pop() { - let kind = arena.expr(id).kind.clone(); - if let ExprKind::Identifier { variable_id } = kind { - if variable_id.is_global() { - let ty = types.get(id).clone(); - let entry = all_types.entry(variable_id.name()).or_default(); - if !entry.contains(&ty) { - entry.push(ty); - } - } - } else { - for child in children_of(id, arena).into_iter().rev() { - stack.push(child); - } - } - } - - all_types + types: &mut TypeTable, +) -> Result<(), RibTypeErrorInternal> { + env_namespace::apply_env_namespace_types(root, arena, types); + env_namespace::validate_global_identifiers(root, arena)?; + Ok(()) } diff --git a/rib-lang/src/type_inference/global_variable_type_binding.rs b/rib-lang/src/type_inference/global_variable_type_binding.rs deleted file mode 100644 index 94ef234..0000000 --- a/rib-lang/src/type_inference/global_variable_type_binding.rs +++ /dev/null @@ -1,253 +0,0 @@ -use crate::expr_arena::{ExprArena, ExprId, ExprKind, TypeTable}; -use crate::type_checker::Path; -use crate::type_checker::PathElem; -use crate::type_inference::expr_visitor::arena::children_of; -use crate::InferredType; -use crate::VariableId; - -#[derive(Clone, Debug)] -pub struct GlobalVariableTypeSpec { - variable_id: VariableId, - path: Path, - inferred_type: InferredType, -} - -impl GlobalVariableTypeSpec { - pub fn variable(&self) -> String { - self.variable_id.name() - } - - // Constructs a new `GlobalVariableTypeSpec`, which associates a specific inferred type - // with a global variable and its nested path. - // - // A path denotes access to nested fields within a variable, where each field - // may be typed explicitly. For example: - // - A specification like `a.*` implies that all fields under `a` are of type `Str`. - // - Similarly, `a.b.*` indicates that all fields under `a.b` are of type `Str`. - // - // Paths are expected to reference at least one nested field. - // - // The type system enforces consistency across declared paths. If contradictory default - // types are specified for the same or overlapping paths, a compilation error will occur. - // - // Parameters: - // - `variable_name`: The name of the root global variable (e.g., `"a"` in `a.b.c`). - // - `path`: A `Path` representing the sequence of nested fields from the root variable - // (e.g., `[b, c]` for `a.b.c`). - // - `inferred_type`: The enforced type (e.g., `Str`, `U64`) for the value - // located at the specified path. - // Note that the inferred_type is applied only to the element that exists after the end of the `path`. - // For example, if the path is `a.b` and the inferred type is `Str`, then the type of `a.b.c` will be `Str` - // and not for `a.b` - pub fn new( - variable_name: &str, - path: Path, - inferred_type: InferredType, - ) -> GlobalVariableTypeSpec { - GlobalVariableTypeSpec { - variable_id: VariableId::global(variable_name.to_string()), - path, - inferred_type, - } - } -} - -pub fn bind_global_variable_types_lowered( - root: ExprId, - arena: &ExprArena, - types: &mut TypeTable, - type_specs: &[GlobalVariableTypeSpec], -) { - for spec in type_specs { - override_type_arena(root, arena, types, spec); - } -} - -fn override_type_arena( - root: ExprId, - arena: &ExprArena, - types: &mut TypeTable, - spec: &GlobalVariableTypeSpec, -) { - let full_path = { - let mut p = spec.path.clone(); - p.push_front(PathElem::Field(spec.variable_id.to_string())); - p - }; - - let mut order = Vec::new(); - collect_post_order_global_var(root, arena, &mut order); - - let mut current_path = full_path.clone(); - let mut previous_id: Option = None; - - for id in order { - let node = arena.expr(id); - match &node.kind { - ExprKind::Identifier { variable_id } => { - if variable_id == &spec.variable_id { - current_path.progress(); - if spec.path.is_empty() { - types.set(id, spec.inferred_type.clone()); - previous_id = None; - current_path = full_path.clone(); - } else { - previous_id = Some(id); - } - } else { - previous_id = None; - current_path = full_path.clone(); - } - } - ExprKind::SelectField { - expr: inner_id, - field, - } => { - if let Some(prev_id) = previous_id { - if *inner_id == prev_id { - if current_path.is_empty() { - types.set(id, spec.inferred_type.clone()); - previous_id = None; - current_path = full_path.clone(); - } else if current_path.current() - == Some(&PathElem::Field(field.to_string())) - { - current_path.progress(); - previous_id = Some(id); - } else { - previous_id = None; - current_path = full_path.clone(); - } - } else { - previous_id = None; - current_path = full_path.clone(); - } - } - } - _ => { - previous_id = None; - current_path = full_path.clone(); - } - } - } -} - -fn collect_post_order_global_var(root: ExprId, arena: &ExprArena, out: &mut Vec) { - let mut stack = vec![(root, false)]; - while let Some((id, visited)) = stack.pop() { - if visited { - out.push(id); - } else { - stack.push((id, true)); - for child in children_of(id, arena).into_iter().rev() { - stack.push((child, false)); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::rib_source_span::SourceSpan; - use crate::{ComponentDependency, Expr, Id, RibCompiler, RibCompilerConfig, TypeName}; - use test_r::test; - - #[test] - fn test_override_types_5() { - let expr = Expr::from_text( - r#" - let res = foo.bar.user-id; - let hello: u64 = foo.bar.number; - hello - "#, - ) - .unwrap(); - - let type_spec = GlobalVariableTypeSpec { - variable_id: VariableId::global("foo".to_string()), - path: Path::from_elems(vec!["bar"]), - inferred_type: InferredType::string(), - }; - - let rib_compiler = RibCompiler::new(RibCompilerConfig::new( - ComponentDependency::default(), - vec![type_spec], - vec![], - )); - - let inferred_expr = rib_compiler.infer_types(expr).unwrap(); - - let expected = Expr::expr_block(vec![ - Expr::Let { - variable_id: VariableId::Local("res".to_string(), Some(Id(0))), - type_annotation: None, - expr: Box::new( - Expr::select_field( - Expr::select_field( - Expr::identifier_global("foo", None).with_inferred_type( - InferredType::record(vec![( - "bar".to_string(), - InferredType::record(vec![ - ("number".to_string(), InferredType::u64()), - ("user-id".to_string(), InferredType::string()), - ]), - )]), - ), - "bar", - None, - ) - .with_inferred_type(InferredType::record(vec![ - ("number".to_string(), InferredType::u64()), - ("user-id".to_string(), InferredType::string()), - ])), - "user-id", - None, - ) - .with_inferred_type(InferredType::string()), - ), - inferred_type: InferredType::tuple(vec![]), - source_span: SourceSpan::default(), - }, - Expr::Let { - variable_id: VariableId::Local("hello".to_string(), Some(Id(0))), - type_annotation: Some(TypeName::U64), - expr: Box::new( - Expr::select_field( - Expr::select_field( - Expr::identifier_global("foo", None).with_inferred_type( - InferredType::record(vec![( - "bar".to_string(), - InferredType::record(vec![ - ("number".to_string(), InferredType::u64()), - ("user-id".to_string(), InferredType::string()), - ]), - )]), - ), - "bar", - None, - ) - .with_inferred_type(InferredType::record(vec![ - ("number".to_string(), InferredType::u64()), - ("user-id".to_string(), InferredType::string()), - ])), - "number", - None, - ) - .with_inferred_type(InferredType::u64()), - ), - inferred_type: InferredType::tuple(vec![]), - source_span: SourceSpan::default(), - }, - Expr::Identifier { - variable_id: VariableId::Local("hello".to_string(), Some(Id(0))), - type_annotation: None, - inferred_type: InferredType::u64(), - source_span: SourceSpan::default(), - }, - ]) - .with_inferred_type(InferredType::u64()); - - assert_eq!(inferred_expr.get_expr(), &expected); - } -} diff --git a/rib-lang/src/type_inference/identify_instance_creation.rs b/rib-lang/src/type_inference/identify_instance_creation.rs index e9ad8e1..6ff84b3 100644 --- a/rib-lang/src/type_inference/identify_instance_creation.rs +++ b/rib-lang/src/type_inference/identify_instance_creation.rs @@ -39,10 +39,13 @@ fn search_for_invalid_instance_declarations_arena( let span = node.source_span.clone(); match &node.kind.clone() { ExprKind::Let { variable_id, .. } => { - if variable_id.name() == "instance" { + if variable_id.name() == "instance" || variable_id.name() == "env" { return Err(CustomError::new( span, - "`instance` is a reserved keyword and cannot be used as a variable.", + format!( + "`{}` is a reserved keyword and cannot be used as a variable.", + variable_id.name() + ), ) .into()); } diff --git a/rib-lang/src/type_inference/inference_fix_point.rs b/rib-lang/src/type_inference/inference_fix_point.rs index 6f92ff0..628e47e 100644 --- a/rib-lang/src/type_inference/inference_fix_point.rs +++ b/rib-lang/src/type_inference/inference_fix_point.rs @@ -39,12 +39,13 @@ mod tests { fn test_fix_point() { let expr = r#" let x: u64 = 1; + let y: u64 = 2; if x == x then x else y "#; let mut expr = Expr::from_text(expr).unwrap(); - expr.infer_types(&ComponentDependency::default(), &vec![], &[]) + expr.infer_types(&ComponentDependency::default(), &[]) .unwrap(); let expected = Expr::expr_block(vec![ Expr::let_binding_with_variable_id( @@ -52,13 +53,18 @@ mod tests { Expr::number_inferred(BigDecimal::from(1), None, InferredType::u64()), Some(TypeName::U64), ), + Expr::let_binding_with_variable_id( + VariableId::local("y", 0), + Expr::number_inferred(BigDecimal::from(2), None, InferredType::u64()), + Some(TypeName::U64), + ), Expr::cond( Expr::equal_to( Expr::identifier_local("x", 0, None).with_inferred_type(InferredType::u64()), Expr::identifier_local("x", 0, None).with_inferred_type(InferredType::u64()), ), Expr::identifier_local("x", 0, None).with_inferred_type(InferredType::u64()), - Expr::identifier_global("y", None).with_inferred_type(InferredType::u64()), + Expr::identifier_local("y", 0, None).with_inferred_type(InferredType::u64()), ) .with_inferred_type(InferredType::u64()), ]) diff --git a/rib-lang/src/type_inference/inferred_expr.rs b/rib-lang/src/type_inference/inferred_expr.rs index 2867f0a..4318c92 100644 --- a/rib-lang/src/type_inference/inferred_expr.rs +++ b/rib-lang/src/type_inference/inferred_expr.rs @@ -2,7 +2,7 @@ use crate::call_type::CallType; use crate::rib_type_error::RibTypeErrorInternal; use crate::{ visit_post_order_rev_mut, ComponentDependency, CustomInstanceSpec, DynamicParsedFunctionName, - Expr, FunctionName, GlobalVariableTypeSpec, + Expr, FunctionName, }; use std::collections::HashSet; @@ -17,16 +17,11 @@ impl InferredExpr { pub fn from_expr( expr: Expr, component_dependency: &ComponentDependency, - global_variable_type_spec: &Vec, custom_instance_spec: &[CustomInstanceSpec], ) -> Result { let mut mutable_expr = expr; - mutable_expr.infer_types( - component_dependency, - global_variable_type_spec, - custom_instance_spec, - )?; + mutable_expr.infer_types(component_dependency, custom_instance_spec)?; Ok(InferredExpr(mutable_expr)) } diff --git a/rib-lang/src/type_inference/initial_phase.rs b/rib-lang/src/type_inference/initial_phase.rs index 1137018..a70d2f6 100644 --- a/rib-lang/src/type_inference/initial_phase.rs +++ b/rib-lang/src/type_inference/initial_phase.rs @@ -4,7 +4,7 @@ use crate::expr_arena::{ExprArena, ExprId, TypeTable}; use crate::rib_type_error::RibTypeErrorInternal; use crate::type_inference as ti; -use crate::{ComponentDependency, CustomInstanceSpec, GlobalVariableTypeSpec}; +use crate::{ComponentDependency, CustomInstanceSpec}; use std::sync::Arc; pub fn run_initial_binding_and_instance_phases( @@ -12,15 +12,8 @@ pub fn run_initial_binding_and_instance_phases( arena: &mut ExprArena, types: &mut TypeTable, component: Arc, - global_variable_type_spec: &[GlobalVariableTypeSpec], custom_instance_spec: &[CustomInstanceSpec], ) -> Result<(), RibTypeErrorInternal> { - ti::global_variable_type_binding::bind_global_variable_types_lowered( - root, - arena, - types, - global_variable_type_spec, - ); ti::type_annotation_binding::bind_type_annotations(root, arena, types); ti::variable_binding::bind_variables_of_list_comprehension(root, arena, types); ti::variable_binding::bind_variables_of_list_reduce(root, arena, types); diff --git a/rib-lang/src/type_inference/mod.rs b/rib-lang/src/type_inference/mod.rs index cc4115f..02904db 100644 --- a/rib-lang/src/type_inference/mod.rs +++ b/rib-lang/src/type_inference/mod.rs @@ -1,10 +1,10 @@ pub use call_arguments_inference::*; pub use custom_instance_spec::*; pub use enum_inference::*; +pub use env_namespace::*; pub use errors::*; pub use expr_visitor::*; pub use global_input_inference::*; -pub use global_variable_type_binding::*; pub use identifier_inference::*; pub use identify_instance_creation::*; pub use inference_fix_point::*; @@ -27,10 +27,10 @@ pub use worker_function_invocation::*; mod call_arguments_inference; mod custom_instance_spec; mod enum_inference; +mod env_namespace; mod errors; mod expr_visitor; mod global_input_inference; -mod global_variable_type_binding; mod identifier_inference; mod identify_instance_creation; mod inference_fix_point; @@ -53,8 +53,6 @@ mod worker_function_invocation; #[cfg(test)] mod tests { use crate::call_type::CallType; - use crate::type_checker::Path; - use crate::type_inference::global_variable_type_binding::GlobalVariableTypeSpec; use crate::type_inference::tests::test_utils::{ call, concat, cond, equal_to, expr_block, get_test_rib_compiler_with, greater_than, greater_than_or_equal_to, identifier, less_than, less_than_or_equal_to, let_binding, @@ -65,7 +63,7 @@ mod tests { use crate::{ ArmPattern, ComponentDependency, DynamicParsedFunctionName, DynamicParsedFunctionReference, Expr, InferredType, InstanceCreationType, InstanceIdentifier, InstanceType, MatchArm, - Number, ParsedFunctionSite, RibCompiler, RibCompilerConfig, TypeName, VariableId, + Number, ParsedFunctionSite, RibCompiler, TypeName, VariableId, }; use bigdecimal::BigDecimal; use std::sync::Arc; @@ -75,75 +73,65 @@ mod tests { #[test] fn test_inference_global_variable_1() { - let rib_expr = r#" + let unknown_global = r#" let res = foo; res "#; - let mut expr = Expr::from_text(rib_expr).unwrap(); - let type_spec = - GlobalVariableTypeSpec::new("foo", Path::from_elems(vec![]), InferredType::string()); - - let with_type_spec = - expr.infer_types(&ComponentDependency::default(), &vec![type_spec], &[]); - - assert!(with_type_spec.is_ok()); - - let mut new_expr = Expr::from_text(rib_expr).unwrap(); + let mut expr = Expr::from_text(unknown_global).unwrap(); + assert!(expr + .infer_types(&ComponentDependency::default(), &[]) + .is_err()); - let without_type_spec = new_expr.infer_types(&ComponentDependency::default(), &vec![], &[]); + let env_global = r#" + let res = env.foo; + res + "#; - assert!(without_type_spec.is_err()) + let mut expr = Expr::from_text(env_global).unwrap(); + assert!(expr + .infer_types(&ComponentDependency::default(), &[]) + .is_ok()); } #[test] fn test_inference_global_variable_2() { let rib_expr = r#" - let res = request.path.user-id; - let hello: u64 = request.path.number; + let res = env.USER_ID; + let hello: string = env.NUMBER; hello "#; let mut expr = Expr::from_text(rib_expr).unwrap(); - let type_spec = GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["path"]), - InferredType::string(), - ); assert!(expr - .infer_types(&ComponentDependency::default(), &vec![type_spec], &[]) + .infer_types(&ComponentDependency::default(), &[]) .is_ok()); } #[test] fn test_inference_global_variable_3() { let rib_expr = r#" - let res1 = request.path.user-id; - let res2 = request.headers.name; - let res3 = request.headers.age; + let res1 = env.USER_ID; + let res2 = env.NAME; + let res3 = env.AGE; "${res1}-${res2}-${res3}" "#; let mut expr = Expr::from_text(rib_expr).unwrap(); - let type_spec = vec![ - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["path"]), - InferredType::string(), - ), - GlobalVariableTypeSpec::new( - "request", - Path::from_elems(vec!["headers"]), - InferredType::string(), - ), - ]; assert!(expr - .infer_types(&ComponentDependency::default(), &type_spec, &[]) + .infer_types(&ComponentDependency::default(), &[]) .is_ok()); } + #[test] + fn test_inference_env_nested_path_rejected() { + // Only `env.` — one segment; `env.a.b` is not supported. + let expr = Expr::from_text(r#"let x = env.a.b; x"#).unwrap(); + assert!(RibCompiler::default().compile(expr).is_err()); + } + #[test] fn test_inference_with_default_number_type_0() { let rib_expr = r#" @@ -160,7 +148,7 @@ mod tests { let mut expr = Expr::from_text(rib_expr).unwrap(); assert!(expr - .infer_types(&ComponentDependency::default(), &vec![], &[]) + .infer_types(&ComponentDependency::default(), &[]) .is_ok()); } @@ -222,7 +210,8 @@ mod tests { #[test] fn test_inference_inline_type_annotation_1() { - let rib_expr = Expr::from_text(r#"foo.bar.baz[0] + 1u32"#).unwrap(); + let rib_expr = + Expr::from_text(r#"let foo = {bar: {baz: [1u32]}}; foo.bar.baz[0] + 1u32"#).unwrap(); let compiler = RibCompiler::default(); @@ -230,7 +219,9 @@ mod tests { assert!(result.is_ok()); - let rib_expr = Expr::from_text(r#"foo.bar.baz[0]: u32 + 1u32"#).unwrap(); + let rib_expr = + Expr::from_text(r#"let foo = {bar: {baz: [1u32]}}; foo.bar.baz[0]: u32 + 1u32"#) + .unwrap(); let result = compiler.compile(rib_expr); assert!(result.is_ok()); @@ -238,28 +229,17 @@ mod tests { #[test] fn test_inference_inline_type_annotation_2() { - let type_spec = GlobalVariableTypeSpec::new( - "foo", - Path::from_elems(vec!["bar"]), - InferredType::string(), - ); - - let rib_compiler = RibCompiler::new(RibCompilerConfig::new( - ComponentDependency::default(), - vec![type_spec], - vec![], - )); + let rib_compiler = RibCompiler::default(); - // by default foo.bar.* will be inferred to be a string (given the above type spec) and - // foo.bar.baz + 1u32 should fail compilation since we are adding string with a u32. - let invalid_rib_expr = Expr::from_text(r#"foo.bar.baz + 1u32"#).unwrap(); + // `env.baz` is a string; adding u32 should fail. + let invalid_rib_expr = Expr::from_text(r#"env.baz + 1u32"#).unwrap(); let result = rib_compiler.compile(invalid_rib_expr); assert!(result.is_err()); - // We inline the type of foo.bar.baz with u32 (over-riding what's given in the type spec) - let valid_rib_expr = Expr::from_text(r#"foo.bar.baz: u32 + 1u32"#).unwrap(); + // Inline annotation can override a string literal for numeric use. + let valid_rib_expr = Expr::from_text(r#""1": u32 + 1u32"#).unwrap(); let result = rib_compiler.compile(valid_rib_expr); assert!(result.is_ok()); @@ -267,25 +247,15 @@ mod tests { #[test] fn test_inference_inline_type_annotation_3() { - let type_spec = - GlobalVariableTypeSpec::new("foo", Path::from_elems(vec![]), InferredType::string()); - - // by default foo will be inferred to be a string (given the above type spec) and - // foo + 1u32 should fail compilation since we are adding string with a u32. - let invalid_rib_expr = Expr::from_text(r#"foo + 1u32"#).unwrap(); + let rib_compiler = RibCompiler::default(); - let rib_compiler = RibCompiler::new(RibCompilerConfig::new( - ComponentDependency::default(), - vec![type_spec], - vec![], - )); + let invalid_rib_expr = Expr::from_text(r#"env.foo + 1u32"#).unwrap(); let result = rib_compiler.compile(invalid_rib_expr); assert!(result.is_err()); - // We inline the type of foo identifier with u32 (over-riding what's given in the type spec) - let valid_rib_expr = Expr::from_text(r#"foo: u32 + 1u32"#).unwrap(); + let valid_rib_expr = Expr::from_text(r#""2": u32 + 1u32"#).unwrap(); let result = rib_compiler.compile(valid_rib_expr); @@ -747,7 +717,7 @@ mod tests { ); let expr = r#" - let user: string = request.body.user-id; + let user: string = env.USER_ID; let query1 = foo; let query2 = bar; let query3 = foo-bar; @@ -813,7 +783,7 @@ mod tests { let expr = r#" let worker = instance(); - let user = request.body.user-id; + let user = 1u64; let query1 = foo(user); let query2 = bar-baz("jon"); let query3 = foo-bar; @@ -1268,7 +1238,7 @@ mod tests { let mut expr = Expr::from_text(expr_str).unwrap(); - expr.infer_types(&ComponentDependency::default(), &vec![], &[]) + expr.infer_types(&ComponentDependency::default(), &[]) .unwrap(); let expected = expr_block( @@ -2172,7 +2142,7 @@ mod tests { get_test_rib_compiler_with("foo", vec![request_type.clone()], return_type); let expr_str = r#" - let x = { body : { id: "bId", name: "bName", titles: request.body.titles, address: request.body.address } }; + let x = { body : { id: "bId", name: "bName", titles: ["a", "b"], address: { street: "st", city: "ct" } } }; let worker = instance("foo"); let result = worker.foo(x); match result { some(value) => "personal-id", none => x.body.titles[1] } @@ -2715,7 +2685,6 @@ mod tests { ) .unwrap(), vec![], - vec![], )) } @@ -2755,7 +2724,6 @@ mod tests { ComponentDependency::from_wit_metadata(component_dependency_key.clone(), &exports) .unwrap(), vec![], - vec![], )) } @@ -2768,26 +2736,15 @@ mod tests { VariableId::local("user", 0), Some(TypeName::Str), select_field( - select_field( - identifier( - VariableId::global("request".to_string()), - None, - InferredType::record(vec![( - "body".to_string(), - InferredType::record(vec![( - "user-id".to_string(), - InferredType::string(), - )]), - )]), - ), - "body".to_string(), + identifier( + VariableId::global("env".to_string()), None, InferredType::record(vec![( - "user-id".to_string(), + "USER_ID".to_string(), InferredType::string(), )]), ), - "user-id".to_string(), + "USER_ID".to_string(), None, InferredType::string(), ), @@ -3186,123 +3143,19 @@ mod tests { ("name".to_string(), Expr::literal("bName")), ( "titles".to_string(), - select_field( - select_field( - identifier( - VariableId::global("request".to_string()), - None, - InferredType::record(vec![( - "body".to_string(), - InferredType::record(vec![ - ( - "address".to_string(), - InferredType::record(vec![ - ( - "street".to_string(), - InferredType::string(), - ), - ( - "city".to_string(), - InferredType::string(), - ), - ]), - ), - ( - "titles".to_string(), - InferredType::list( - InferredType::string(), - ), - ), - ]), - )]), - ), - "body".to_string(), - None, - InferredType::record(vec![ - ( - "address".to_string(), - InferredType::record(vec![ - ( - "street".to_string(), - InferredType::string(), - ), - ( - "city".to_string(), - InferredType::string(), - ), - ]), - ), - ( - "titles".to_string(), - InferredType::list( - InferredType::string(), - ), - ), - ]), - ), - "titles".to_string(), + sequence( + vec![Expr::literal("a"), Expr::literal("b")], None, InferredType::list(InferredType::string()), ), ), ( "address".to_string(), - select_field( - select_field( - identifier( - VariableId::global("request".to_string()), - None, - InferredType::record(vec![( - "body".to_string(), - InferredType::record(vec![ - ( - "address".to_string(), - InferredType::record(vec![ - ( - "street".to_string(), - InferredType::string(), - ), - ( - "city".to_string(), - InferredType::string(), - ), - ]), - ), - ( - "titles".to_string(), - InferredType::list( - InferredType::string(), - ), - ), - ]), - )]), - ), - "body".to_string(), - None, - InferredType::record(vec![ - ( - "address".to_string(), - InferredType::record(vec![ - ( - "street".to_string(), - InferredType::string(), - ), - ( - "city".to_string(), - InferredType::string(), - ), - ]), - ), - ( - "titles".to_string(), - InferredType::list( - InferredType::string(), - ), - ), - ]), - ), - "address".to_string(), - None, + record( + vec![ + ("street".to_string(), Expr::literal("st")), + ("city".to_string(), Expr::literal("ct")), + ], InferredType::record(vec![ ("street".to_string(), InferredType::string()), ("city".to_string(), InferredType::string()), diff --git a/rib-lang/src/type_inference/rib_input_type.rs b/rib-lang/src/type_inference/rib_input_type.rs index 6ebaaeb..f26139f 100644 --- a/rib-lang/src/type_inference/rib_input_type.rs +++ b/rib-lang/src/type_inference/rib_input_type.rs @@ -3,8 +3,8 @@ use crate::{try_visit_post_order_rev_mut, Expr, InferredExpr, RibCompilationErro use serde::{Deserialize, Serialize}; use std::collections::HashMap; -// RibInputTypeInfo refers to the required global inputs to a RibScript -// with its type information. Example: `request` variable which should be of the type `Record`. +/// Declared environment bindings used by the script: each `env.` is [`WitType::Str`]. +/// The `` matches the host / OS / [`crate::RibInput`] key exactly (e.g. `env.TOKEN_ID` → key `TOKEN_ID`). #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RibInputTypeInfo { @@ -26,30 +26,35 @@ impl RibInputTypeInfo { ) -> Result { let mut expr = inferred_expr.get_expr().clone(); - let mut global_variables = HashMap::new(); + let mut types = HashMap::new(); try_visit_post_order_rev_mut(&mut expr, &mut |expr| { - if let Expr::Identifier { - variable_id, + if let Expr::SelectField { + expr: inner, + field, inferred_type, .. } = &*expr { - if variable_id.is_global() { - let analysed_type = WitType::try_from(inferred_type).map_err(|e| { - RibCompilationError::RibStaticAnalysisError(format!( - "failed to convert inferred type to wit type: {e}" - )) - })?; - - global_variables.insert(variable_id.name(), analysed_type); + if let Expr::Identifier { + variable_id, + inferred_type: _, + .. + } = inner.as_ref() + { + if variable_id.is_global() && variable_id.name() == "env" { + let analysed_type = WitType::try_from(inferred_type).map_err(|e| { + RibCompilationError::RibStaticAnalysisError(format!( + "failed to convert inferred type to wit type: {e}" + )) + })?; + types.insert(format!("env.{field}"), analysed_type); + } } } Ok::<(), RibCompilationError>(()) })?; - Ok(RibInputTypeInfo { - types: global_variables, - }) + Ok(RibInputTypeInfo { types }) } } diff --git a/rib-repl/src/repl_printer.rs b/rib-repl/src/repl_printer.rs index 1a12047..02dfa1c 100644 --- a/rib-repl/src/repl_printer.rs +++ b/rib-repl/src/repl_printer.rs @@ -375,23 +375,6 @@ fn print_rib_compilation_error(error: &RibCompilationError) { println!("{} {}", "[rib static analysis error]".red(), msg.red()); } - RibCompilationError::UnsupportedGlobalInput { - invalid_global_inputs: found, - valid_global_inputs: expected, - } => { - println!( - "{} {} {}", - "[unsupported input]".red(), - "found:".yellow(), - found.join(", ").white() - ); - println!( - "{} {} {}", - "[supported inputs]".green(), - "expected:".yellow(), - expected.join(", ").white() - ); - } RibCompilationError::RibTypeError(compilation_error) => { let cause = &compilation_error.cause; let position = &compilation_error.source_span; diff --git a/rib-repl/src/rib_repl.rs b/rib-repl/src/rib_repl.rs index 041eed8..a857b97 100644 --- a/rib-repl/src/rib_repl.rs +++ b/rib-repl/src/rib_repl.rs @@ -120,7 +120,6 @@ impl RibRepl { config.worker_function_invoke, RibCompiler::new(RibCompilerConfig::new( bundle.component.clone(), - vec![], bundle.custom_instance_spec.clone(), )), history_file_path.clone(),