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