obeli_sk_wasm_workers/
lib.rs1use 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}