Skip to main content

obeli_sk_wasm_workers/
lib.rs

1use concepts::{ComponentType, FunctionFqn, FunctionMetadata, StrVariant};
2use std::{error::Error, fmt::Debug, path::Path};
3use tracing::{debug, trace};
4use tracing_error::SpanTrace;
5use utils::wasm_tools::{self, DecodeError, ExIm, WasmComponent};
6
7pub mod activity;
8pub mod component_logger;
9pub mod engines;
10pub mod epoch_ticker;
11pub(crate) mod http_hooks;
12pub mod http_request_policy;
13pub(crate) mod js_wit_builder;
14pub(crate) mod js_worker_utils;
15pub mod log_db_forwarder;
16pub(crate) mod policy_builder;
17pub mod preopens_cleaner;
18pub mod registry;
19pub mod std_output_stream;
20#[cfg(any(test, feature = "test"))]
21pub mod testing_fn_registry;
22pub mod webhook;
23pub mod workflow;
24
25#[derive(thiserror::Error, Debug)]
26pub enum WasmFileError {
27    #[error("cannot decode: {0}")]
28    DecodeError(
29        #[from]
30        #[source]
31        wasm_tools::DecodeError,
32    ),
33    #[error("linking error - {reason}, details: {err}")]
34    LinkingError {
35        reason: StrVariant,
36        #[source]
37        err: Box<dyn Error + Send + Sync>,
38        context: SpanTrace,
39    },
40}
41impl WasmFileError {
42    pub fn linking_error(
43        reason: impl Into<StrVariant>,
44        error: impl Into<Box<dyn Error + Send + Sync>>,
45    ) -> WasmFileError {
46        WasmFileError::LinkingError {
47            reason: reason.into(),
48            err: error.into(),
49            context: SpanTrace::capture(),
50        }
51    }
52}
53
54pub mod envvar {
55    #[derive(Clone, derive_more::Debug)]
56    pub struct EnvVar {
57        pub key: String,
58        #[debug(skip)]
59        pub val: String,
60    }
61}
62
63#[derive(derive_more::Debug, Clone)]
64pub struct RunnableComponent {
65    #[debug(skip)]
66    pub wasmtime_component: wasmtime::component::Component,
67    pub wasm_component: WasmComponent,
68}
69impl RunnableComponent {
70    pub fn new<P: AsRef<Path>>(
71        wasm_path: P,
72        engine: &wasmtime::Engine,
73        component_type: ComponentType,
74    ) -> Result<Self, DecodeError> {
75        let wasm_path = wasm_path.as_ref();
76        let wasm_component = WasmComponent::new(wasm_path, component_type)?;
77        trace!("Decoding using wasmtime");
78        let wasmtime_component = {
79            let stopwatch = std::time::Instant::now();
80            let wasmtime_component = wasmtime::component::Component::from_file(engine, wasm_path)
81                .map_err(|err| {
82                DecodeError::new_with_source(
83                    format!("cannot parse {wasm_path:?} using wasmtime"),
84                    err,
85                )
86            })?;
87            debug!("Parsed with wasmtime in {:?}", stopwatch.elapsed());
88            wasmtime_component
89        };
90        Ok(Self {
91            wasmtime_component,
92            wasm_component,
93        })
94    }
95
96    pub fn index_exported_functions(
97        wasmtime_component: &wasmtime::component::Component,
98        exim: &ExIm,
99    ) -> Result<
100        hashbrown::HashMap<FunctionFqn, wasmtime::component::ComponentExportIndex>,
101        DecodeError,
102    > {
103        let mut exported_ffqn_to_index = hashbrown::HashMap::new();
104        for FunctionMetadata { ffqn, .. } in exim.get_exports(false) {
105            let Some(ifc_export_index) = wasmtime_component.get_export_index(None, &ffqn.ifc_fqn)
106            else {
107                return Err(DecodeError::new_without_source(format!(
108                    "cannot find exported interface {ffqn}"
109                )));
110            };
111            let Some(fn_export_index) =
112                wasmtime_component.get_export_index(Some(&ifc_export_index), &ffqn.function_name)
113            else {
114                return Err(DecodeError::new_without_source(format!(
115                    "cannot find exported function {ffqn}"
116                )));
117            };
118            exported_ffqn_to_index.insert(ffqn.clone(), fn_export_index);
119        }
120        Ok(exported_ffqn_to_index)
121    }
122}
123
124#[cfg(test)]
125pub(crate) mod tests {
126
127    mod populate_codegen_cache {
128        use crate::{
129            activity::activity_worker::test::compile_activity,
130            workflow::workflow_worker::test::compile_workflow,
131        };
132
133        #[rstest::rstest(wasm_path => [
134            test_programs_fibo_activity_builder::TEST_PROGRAMS_FIBO_ACTIVITY,
135            test_programs_http_get_activity_builder::TEST_PROGRAMS_HTTP_GET_ACTIVITY,
136            test_programs_sleep_activity_builder::TEST_PROGRAMS_SLEEP_ACTIVITY,
137            test_programs_dir_activity_builder::TEST_PROGRAMS_DIR_ACTIVITY,
138            test_programs_process_activity_builder::TEST_PROGRAMS_PROCESS_ACTIVITY,
139            activity_js_runtime_builder::ACTIVITY_JS_RUNTIME,
140            ])]
141        #[tokio::test]
142        async fn activity(wasm_path: &str) {
143            compile_activity(wasm_path).await;
144        }
145
146        #[rstest::rstest(wasm_path => [
147            test_programs_fibo_workflow_builder::TEST_PROGRAMS_FIBO_WORKFLOW,
148            test_programs_http_get_workflow_builder::TEST_PROGRAMS_HTTP_GET_WORKFLOW,
149            test_programs_sleep_workflow_builder::TEST_PROGRAMS_SLEEP_WORKFLOW,
150            workflow_js_runtime_builder::WORKFLOW_JS_RUNTIME,
151            ])]
152        #[tokio::test]
153        async fn workflow(wasm_path: &str) {
154            compile_workflow(wasm_path).await;
155        }
156
157        #[rstest::rstest(wasm_path => [
158            test_programs_fibo_webhook_builder::TEST_PROGRAMS_FIBO_WEBHOOK,
159            webhook_js_runtime_builder::WEBHOOK_JS_RUNTIME,
160            ])]
161        #[test]
162        fn webhook(wasm_path: &str) {
163            crate::webhook::webhook_trigger::tests::compile_webhook(wasm_path);
164        }
165    }
166}