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