golem_rust/
lib.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
15use std::fmt;
16use std::fmt::{Display, Formatter};
17use std::str::FromStr;
18
19#[cfg(test)]
20test_r::enable!();
21
22pub use uuid::Uuid;
23
24pub mod bindings {
25    use wit_bindgen::generate;
26
27    generate!({
28        path: "wit",
29        world: "golem-rust",
30        generate_all,
31        generate_unused_types: true,
32        pub_export_macro: true,
33        with: {
34            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
35            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
36            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock
37        }
38    });
39}
40
41#[cfg(feature = "export_load_snapshot")]
42pub mod load_snapshot {
43    use wit_bindgen::generate;
44
45    generate!({
46        path: "wit",
47        world: "golem-rust-load-snapshot",
48        generate_all,
49        generate_unused_types: true,
50        pub_export_macro: true,
51        with: {
52            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
53            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
54            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock,
55
56            "golem:api/host@1.3.0": crate::bindings::golem::api::host,
57            "golem:api/oplog@1.3.0": crate::bindings::golem::api::oplog,
58            "golem:api/context@1.3.0": crate::bindings::golem::api::context,
59            "golem:durability/durability@1.3.0": crate::bindings::golem::durability::durability,
60            "golem:rdbms/mysql@0.0.1": crate::bindings::golem::rdbms::mysql,
61            "golem:rdbms/postgres@0.0.1": crate::bindings::golem::rdbms::postgres,
62            "golem:rdbms/types@0.0.1": crate::bindings::golem::rdbms::types,
63            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
64            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
65            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
66            "wasi:clocks/monotonic-clock@0.2.3": crate::bindings::wasi::clocks::monotonic_clock,
67            "wasi:filesystem/preopens@0.2.3": crate::bindings::wasi::filesystem::preopens,
68            "wasi:filesystem/types@0.2.3": crate::bindings::wasi::filesystem::types,
69            "wasi:http/types@0.2.3": crate::bindings::wasi::http::types,
70            "wasi:http/outgoing-handler@0.2.3": crate::bindings::wasi::http::outgoing_handler,
71            "wasi:io/error@0.2.3": crate::bindings::wasi::io::error,
72            "wasi:io/streams@0.2.3": crate::bindings::wasi::io::streams,
73            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
74            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
75            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
76            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
77            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
78            "wasi:sockets/ip-name-lookup@0.2.3": crate::bindings::wasi::sockets::ip_name_lookup,
79            "wasi:sockets/instance-network@0.2.3": crate::bindings::wasi::sockets::instance_network,
80            "wasi:sockets/network@0.2.3": crate::bindings::wasi::sockets::network,
81        }
82    });
83
84    pub use __export_golem_rust_load_snapshot_impl as export_load_snapshot;
85}
86
87#[cfg(feature = "export_save_snapshot")]
88pub mod save_snapshot {
89    use wit_bindgen::generate;
90
91    generate!({
92        path: "wit",
93        world: "golem-rust-save-snapshot",
94        generate_all,
95        generate_unused_types: true,
96        pub_export_macro: true,
97        with: {
98            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
99            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
100            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock,
101
102            "golem:api/host@1.3.0": crate::bindings::golem::api::host,
103            "golem:api/oplog@1.3.0": crate::bindings::golem::api::oplog,
104            "golem:api/context@1.3.0": crate::bindings::golem::api::context,
105            "golem:durability/durability@1.3.0": crate::bindings::golem::durability::durability,
106            "golem:rdbms/mysql@0.0.1": crate::bindings::golem::rdbms::mysql,
107            "golem:rdbms/postgres@0.0.1": crate::bindings::golem::rdbms::postgres,
108            "golem:rdbms/types@0.0.1": crate::bindings::golem::rdbms::types,
109            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
110            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
111            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
112            "wasi:clocks/monotonic-clock@0.2.3": crate::bindings::wasi::clocks::monotonic_clock,
113            "wasi:filesystem/preopens@0.2.3": crate::bindings::wasi::filesystem::preopens,
114            "wasi:filesystem/types@0.2.3": crate::bindings::wasi::filesystem::types,
115            "wasi:http/types@0.2.3": crate::bindings::wasi::http::types,
116            "wasi:http/outgoing-handler@0.2.3": crate::bindings::wasi::http::outgoing_handler,
117            "wasi:io/error@0.2.3": crate::bindings::wasi::io::error,
118            "wasi:io/streams@0.2.3": crate::bindings::wasi::io::streams,
119            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
120            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
121            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
122            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
123            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
124            "wasi:sockets/ip-name-lookup@0.2.3": crate::bindings::wasi::sockets::ip_name_lookup,
125            "wasi:sockets/instance-network@0.2.3": crate::bindings::wasi::sockets::instance_network,
126            "wasi:sockets/network@0.2.3": crate::bindings::wasi::sockets::network,
127        }
128    });
129
130    pub use __export_golem_rust_save_snapshot_impl as export_save_snapshot;
131}
132
133#[cfg(feature = "export_golem_agentic")]
134pub mod golem_agentic {
135    use wit_bindgen::generate;
136
137    generate!({
138        path: "wit",
139        world: "golem-agentic",
140        generate_all,
141        generate_unused_types: true,
142        pub_export_macro: true,
143        with: {
144            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
145            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
146            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock,
147
148            "golem:api/host@1.3.0": crate::bindings::golem::api::host,
149            "golem:api/oplog@1.3.0": crate::bindings::golem::api::oplog,
150            "golem:api/context@1.3.0": crate::bindings::golem::api::context,
151            "golem:durability/durability@1.3.0": crate::bindings::golem::durability::durability,
152            "golem:rdbms/mysql@0.0.1": crate::bindings::golem::rdbms::mysql,
153            "golem:rdbms/postgres@0.0.1": crate::bindings::golem::rdbms::postgres,
154            "golem:rdbms/types@0.0.1": crate::bindings::golem::rdbms::types,
155            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
156            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
157            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
158            "wasi:clocks/monotonic-clock@0.2.3": crate::bindings::wasi::clocks::monotonic_clock,
159            "wasi:filesystem/preopens@0.2.3": crate::bindings::wasi::filesystem::preopens,
160            "wasi:filesystem/types@0.2.3": crate::bindings::wasi::filesystem::types,
161            "wasi:http/types@0.2.3": crate::bindings::wasi::http::types,
162            "wasi:http/outgoing-handler@0.2.3": crate::bindings::wasi::http::outgoing_handler,
163            "wasi:io/error@0.2.3": crate::bindings::wasi::io::error,
164            "wasi:io/streams@0.2.3": crate::bindings::wasi::io::streams,
165            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
166            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
167            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
168            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
169            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
170            "wasi:sockets/ip-name-lookup@0.2.3": crate::bindings::wasi::sockets::ip_name_lookup,
171            "wasi:sockets/instance-network@0.2.3": crate::bindings::wasi::sockets::instance_network,
172            "wasi:sockets/network@0.2.3": crate::bindings::wasi::sockets::network,
173        }
174    });
175
176    pub use __export_golem_agentic_impl as export_golem_agentic;
177}
178
179#[cfg(feature = "golem_ai")]
180pub mod golem_ai {
181    use wit_bindgen::generate;
182    generate!({
183        path: "wit",
184        world: "golem-ai",
185        generate_all,
186        generate_unused_types: true,
187        additional_derives: [crate::Schema],
188        pub_export_macro: true,
189        with: {
190            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
191            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
192            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock,
193
194            "golem:api/host@1.3.0": crate::bindings::golem::api::host,
195            "golem:api/oplog@1.3.0": crate::bindings::golem::api::oplog,
196            "golem:api/context@1.3.0": crate::bindings::golem::api::context,
197            "golem:durability/durability@1.3.0": crate::bindings::golem::durability::durability,
198            "golem:rdbms/mysql@0.0.1": crate::bindings::golem::rdbms::mysql,
199            "golem:rdbms/postgres@0.0.1": crate::bindings::golem::rdbms::postgres,
200            "golem:rdbms/types@0.0.1": crate::bindings::golem::rdbms::types,
201            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
202            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
203            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
204            "wasi:clocks/monotonic-clock@0.2.3": crate::bindings::wasi::clocks::monotonic_clock,
205            "wasi:filesystem/preopens@0.2.3": crate::bindings::wasi::filesystem::preopens,
206            "wasi:filesystem/types@0.2.3": crate::bindings::wasi::filesystem::types,
207            "wasi:http/types@0.2.3": crate::bindings::wasi::http::types,
208            "wasi:http/outgoing-handler@0.2.3": crate::bindings::wasi::http::outgoing_handler,
209            "wasi:io/error@0.2.3": crate::bindings::wasi::io::error,
210            "wasi:io/streams@0.2.3": crate::bindings::wasi::io::streams,
211            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
212            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
213            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
214            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
215            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
216            "wasi:sockets/ip-name-lookup@0.2.3": crate::bindings::wasi::sockets::ip_name_lookup,
217            "wasi:sockets/instance-network@0.2.3": crate::bindings::wasi::sockets::instance_network,
218            "wasi:sockets/network@0.2.3": crate::bindings::wasi::sockets::network,
219        }
220    });
221}
222
223#[cfg(feature = "export_golem_agentic")]
224pub use ctor;
225
226#[cfg(feature = "export_golem_agentic")]
227pub use async_trait;
228
229#[cfg(feature = "export_oplog_processor")]
230pub mod oplog_processor {
231    use wit_bindgen::generate;
232
233    generate!({
234        path: "wit",
235        world: "golem-rust-oplog-processor",
236        generate_all,
237        generate_unused_types: true,
238        pub_export_macro: true,
239        with: {
240            "golem:rpc/types@0.2.2": golem_wasm::golem_rpc_0_2_x::types,
241            "wasi:io/poll@0.2.3": golem_wasm::wasi::io::poll,
242            "wasi:clocks/wall-clock@0.2.3": golem_wasm::wasi::clocks::wall_clock,
243
244            "golem:api/host@1.3.0": crate::bindings::golem::api::host,
245            "golem:api/oplog@1.3.0": crate::bindings::golem::api::oplog,
246            "golem:api/context@1.3.0": crate::bindings::golem::api::context,
247            "golem:durability/durability@1.3.0": crate::bindings::golem::durability::durability,
248            "golem:rdbms/mysql@0.0.1": crate::bindings::golem::rdbms::mysql,
249            "golem:rdbms/postgres@0.0.1": crate::bindings::golem::rdbms::postgres,
250            "golem:rdbms/types@0.0.1": crate::bindings::golem::rdbms::types,
251            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
252            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
253            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
254            "wasi:clocks/monotonic-clock@0.2.3": crate::bindings::wasi::clocks::monotonic_clock,
255            "wasi:filesystem/preopens@0.2.3": crate::bindings::wasi::filesystem::preopens,
256            "wasi:filesystem/types@0.2.3": crate::bindings::wasi::filesystem::types,
257            "wasi:http/types@0.2.3": crate::bindings::wasi::http::types,
258            "wasi:http/outgoing-handler@0.2.3": crate::bindings::wasi::http::outgoing_handler,
259            "wasi:io/error@0.2.3": crate::bindings::wasi::io::error,
260            "wasi:io/streams@0.2.3": crate::bindings::wasi::io::streams,
261            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
262            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
263            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
264            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
265            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
266            "wasi:sockets/ip-name-lookup@0.2.3": crate::bindings::wasi::sockets::ip_name_lookup,
267            "wasi:sockets/instance-network@0.2.3": crate::bindings::wasi::sockets::instance_network,
268            "wasi:sockets/network@0.2.3": crate::bindings::wasi::sockets::network,
269        }
270    });
271
272    pub use __export_golem_rust_oplog_processor_impl as export_oplog_processor;
273}
274
275#[cfg(feature = "export_golem_agentic")]
276pub mod agentic;
277
278#[cfg(feature = "durability")]
279pub mod durability;
280
281#[cfg(feature = "json")]
282mod json;
283
284#[cfg(feature = "json")]
285pub use json::*;
286
287mod transaction;
288pub mod value_and_type;
289
290use bindings::golem::api::host::*;
291
292pub use golem_wasm as wasm_rpc;
293
294pub use bindings::golem::api::host::{fork, oplog_commit};
295pub use bindings::golem::api::host::{ForkResult, PersistenceLevel};
296
297pub use transaction::*;
298
299#[cfg(feature = "macro")]
300pub use golem_rust_macro::*;
301
302impl Display for PromiseId {
303    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
304        write!(f, "{}/{}", self.agent_id, self.oplog_idx)
305    }
306}
307
308impl FromStr for PromiseId {
309    type Err = String;
310
311    fn from_str(s: &str) -> Result<Self, Self::Err> {
312        let parts: Vec<&str> = s.split('/').collect();
313        if parts.len() == 2 {
314            let agent_id = AgentId::from_str(parts[0]).map_err(|_| {
315                format!("invalid agent id: {s} - expected format: <component_id>/<agent_id>")
316            })?;
317            let oplog_idx = parts[1]
318                .parse()
319                .map_err(|_| format!("invalid oplog index: {s} - expected integer"))?;
320            Ok(Self {
321                agent_id,
322                oplog_idx,
323            })
324        } else {
325            Err(format!(
326                "invalid promise id: {s} - expected format: <agent_id>/<oplog_idx>"
327            ))
328        }
329    }
330}
331
332#[derive(Clone, Debug, PartialEq)]
333pub struct RetryPolicy {
334    pub max_attempts: u32,
335    pub min_delay: std::time::Duration,
336    pub max_delay: std::time::Duration,
337    pub multiplier: f64,
338    pub max_jitter_factor: Option<f64>,
339}
340
341impl From<bindings::golem::api::host::RetryPolicy> for RetryPolicy {
342    fn from(value: bindings::golem::api::host::RetryPolicy) -> Self {
343        Self {
344            max_attempts: value.max_attempts,
345            min_delay: std::time::Duration::from_nanos(value.min_delay),
346            max_delay: std::time::Duration::from_nanos(value.max_delay),
347            multiplier: value.multiplier,
348            max_jitter_factor: value.max_jitter_factor,
349        }
350    }
351}
352
353impl From<RetryPolicy> for bindings::golem::api::host::RetryPolicy {
354    fn from(val: RetryPolicy) -> Self {
355        bindings::golem::api::host::RetryPolicy {
356            max_attempts: val.max_attempts,
357            min_delay: val.min_delay.as_nanos() as u64,
358            max_delay: val.max_delay.as_nanos() as u64,
359            multiplier: val.multiplier,
360            max_jitter_factor: val.max_jitter_factor,
361        }
362    }
363}
364
365pub struct PersistenceLevelGuard {
366    original_level: PersistenceLevel,
367}
368
369impl Drop for PersistenceLevelGuard {
370    fn drop(&mut self) {
371        set_oplog_persistence_level(self.original_level);
372    }
373}
374
375/// Temporarily sets the oplog persistence level to the given value.
376///
377/// When the returned guard is dropped, the original persistence level is restored.
378#[must_use]
379pub fn use_persistence_level(level: PersistenceLevel) -> PersistenceLevelGuard {
380    let original_level = get_oplog_persistence_level();
381    set_oplog_persistence_level(level);
382    PersistenceLevelGuard { original_level }
383}
384
385/// Executes the given function with the oplog persistence level set to the given value.
386pub fn with_persistence_level<R>(level: PersistenceLevel, f: impl FnOnce() -> R) -> R {
387    let _guard = use_persistence_level(level);
388    f()
389}
390
391pub struct IdempotenceModeGuard {
392    original: bool,
393}
394
395impl Drop for IdempotenceModeGuard {
396    fn drop(&mut self) {
397        set_idempotence_mode(self.original);
398    }
399}
400
401/// Temporarily sets the idempotence mode to the given value.
402///
403/// When the returned guard is dropped, the original idempotence mode is restored.
404#[must_use]
405pub fn use_idempotence_mode(mode: bool) -> IdempotenceModeGuard {
406    let original = get_idempotence_mode();
407    set_idempotence_mode(mode);
408    IdempotenceModeGuard { original }
409}
410
411/// Executes the given function with the idempotence mode set to the given value.
412pub fn with_idempotence_mode<R>(mode: bool, f: impl FnOnce() -> R) -> R {
413    let _guard = use_idempotence_mode(mode);
414    f()
415}
416
417/// Generates an idempotency key. This operation will never be replayed —
418/// i.e. not only is this key generated, but it is persisted and committed, such that the key can be used in third-party systems (e.g. payment processing)
419/// to introduce idempotence.
420pub fn generate_idempotency_key() -> uuid::Uuid {
421    Into::into(bindings::golem::api::host::generate_idempotency_key())
422}
423
424pub struct RetryPolicyGuard {
425    original: RetryPolicy,
426}
427
428impl Drop for RetryPolicyGuard {
429    fn drop(&mut self) {
430        set_retry_policy(Into::into(self.original.clone()));
431    }
432}
433
434/// Temporarily sets the retry policy to the given value.
435///
436/// When the returned guard is dropped, the original retry policy is restored.
437#[must_use]
438pub fn use_retry_policy(policy: RetryPolicy) -> RetryPolicyGuard {
439    let original = Into::into(get_retry_policy());
440    set_retry_policy(Into::into(policy));
441    RetryPolicyGuard { original }
442}
443
444/// Executes the given function with the retry policy set to the given value.
445pub fn with_retry_policy<R>(policy: RetryPolicy, f: impl FnOnce() -> R) -> R {
446    let _guard = use_retry_policy(policy);
447    f()
448}
449
450pub struct AtomicOperationGuard {
451    begin: OplogIndex,
452}
453
454impl Drop for AtomicOperationGuard {
455    fn drop(&mut self) {
456        mark_end_operation(self.begin);
457    }
458}
459
460/// Marks a block as an atomic operation
461///
462/// When the returned guard is dropped, the operation gets committed.
463/// In case of a failure, the whole operation will be re-executed during retry.
464#[must_use]
465pub fn mark_atomic_operation() -> AtomicOperationGuard {
466    let begin = mark_begin_operation();
467    AtomicOperationGuard { begin }
468}
469
470/// Executes the given function as an atomic operation.
471///
472/// In case of a failure, the whole operation will be re-executed during retry.
473pub fn atomically<T>(f: impl FnOnce() -> T) -> T {
474    let _guard = mark_atomic_operation();
475    f()
476}