rib/compiler/
mod.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub use byte_code::*;
16pub use compiler_output::*;
17use golem_wasm_ast::analysis::AnalysedExport;
18pub use ir::*;
19use std::fmt::Display;
20pub use type_with_unit::*;
21pub use worker_functions_in_rib::*;
22
23use crate::rib_compilation_error::RibCompilationError;
24use crate::type_registry::FunctionTypeRegistry;
25use crate::{Expr, GlobalVariableTypeSpec, InferredExpr, RibInputTypeInfo, RibOutputTypeInfo};
26
27mod byte_code;
28mod compiler_output;
29mod desugar;
30mod ir;
31mod type_with_unit;
32mod worker_functions_in_rib;
33
34pub fn compile(
35    expr: Expr,
36    export_metadata: &Vec<AnalysedExport>,
37) -> Result<CompilerOutput, RibError> {
38    compile_with_restricted_global_variables(expr, export_metadata, None, &vec![])
39}
40
41// Rib allows global input variables, however, we can choose to fail compilation
42// if they don't fall under a pre-defined set of global variables. If nothing is specified,
43// then it implies, any names can be a global variable in Rib. Example: `foo`.
44// Along with this, we can explicitly specify the types of certain global variables using `GlobalVariableTypeSpec`.
45// `GlobalVariableTypeSpec` is a compiler configuration that customises it's behaviour.
46// Example:  request.path.*` should be always a `string`.
47// Not all global variables require a type specification.
48pub fn compile_with_restricted_global_variables(
49    expr: Expr,
50    export_metadata: &Vec<AnalysedExport>,
51    allowed_global_variables: Option<Vec<String>>,
52    global_variable_type_spec: &Vec<GlobalVariableTypeSpec>,
53) -> Result<CompilerOutput, RibError> {
54    for info in global_variable_type_spec {
55        if !info.variable_id.is_global() {
56            return Err(RibError::InternalError(format!(
57                "variable {} in the type spec is not a global variable",
58                info.variable_id
59            )));
60        }
61    }
62
63    let type_registry = FunctionTypeRegistry::from_export_metadata(export_metadata);
64    let inferred_expr = InferredExpr::from_expr(expr, &type_registry, global_variable_type_spec)?;
65
66    let function_calls_identified =
67        WorkerFunctionsInRib::from_inferred_expr(&inferred_expr, &type_registry)?;
68
69    let global_input_type_info = RibInputTypeInfo::from_expr(&inferred_expr)?;
70
71    let output_type_info = RibOutputTypeInfo::from_expr(&inferred_expr)?;
72
73    if let Some(allowed_global_variables) = &allowed_global_variables {
74        let mut un_allowed_variables = vec![];
75
76        for (name, _) in global_input_type_info.types.iter() {
77            if !allowed_global_variables.contains(name) {
78                un_allowed_variables.push(name.clone());
79            }
80        }
81
82        if !un_allowed_variables.is_empty() {
83            return Err(RibError::InvalidRibScript(format!(
84                "unexpected global variables: {}. make sure only these variables are used as global input: {}",
85                un_allowed_variables.join(", "),
86                allowed_global_variables.join(", ")
87            )));
88        }
89    }
90
91    let byte_code = RibByteCode::from_expr(&inferred_expr).map_err(|e| {
92        RibError::InternalError(format!(
93            "failed to convert inferred expression to byte code: {}",
94            e
95        ))
96    })?;
97
98    Ok(CompilerOutput {
99        worker_invoke_calls: function_calls_identified,
100        byte_code,
101        rib_input_type_info: global_input_type_info,
102        rib_output_type_info: Some(output_type_info),
103    })
104}
105
106#[derive(Debug, Clone)]
107pub enum RibError {
108    InternalError(String),
109    RibCompilationError(RibCompilationError),
110    InvalidRibScript(String),
111}
112
113impl From<RibCompilationError> for RibError {
114    fn from(err: RibCompilationError) -> Self {
115        RibError::RibCompilationError(err)
116    }
117}
118
119impl Display for RibError {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        match self {
122            RibError::InternalError(msg) => write!(f, "rib internal error: {}", msg),
123            RibError::RibCompilationError(err) => write!(f, "{}", err),
124            RibError::InvalidRibScript(msg) => write!(f, "invalid rib script: {}", msg),
125        }
126    }
127}