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