golem_wasm/
lib.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.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://license.golem.cloud/LICENSE
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.
14use chrono::{DateTime, Utc};
15
16#[allow(unused)]
17#[rustfmt::skip]
18#[cfg(not(feature = "host"))]
19#[cfg(feature = "stub")]
20mod bindings;
21
22#[cfg(test)]
23test_r::enable!();
24
25pub mod analysis;
26
27/// Implements bincode encoders and decoders for WitValue instances
28#[cfg(feature = "host")]
29pub mod bincode;
30
31/// A builder interface for WitValue instances
32#[cfg(any(feature = "host", feature = "stub"))]
33mod builder;
34
35/// Extension methods for extracting values from WitValue instances
36#[cfg(any(feature = "host", feature = "stub"))]
37mod extractor;
38
39/// Conversion to and from JSON, in the presence of golem-wasm-ast generated type information
40#[cfg(feature = "host")]
41pub mod json;
42
43/// The metadata module defines data structures for representing various metadata extracted from WASM binaries.
44///
45/// This module is optional and can be enabled with the `metadata` feature flag. It is enabled by default.
46#[cfg(feature = "host")]
47pub mod metadata;
48
49/// Poem OpenAPI integration for some types
50#[cfg(feature = "host")]
51pub mod poem;
52
53/// Protobuf-defined value types and conversion to them
54#[cfg(feature = "host")]
55pub mod protobuf;
56
57/// Serde instances for WitValue
58#[cfg(feature = "host")]
59pub mod serde;
60
61/// Conversion to/from the WAVE format
62#[cfg(feature = "host")]
63mod text;
64
65mod value_and_type;
66
67/// Conversion to/from wasmtime's value representation
68#[cfg(feature = "host")]
69pub mod wasmtime;
70
71#[cfg(any(feature = "host", feature = "stub"))]
72use crate::builder::WitValueBuilder;
73
74#[cfg(any(feature = "host", feature = "stub"))]
75pub use builder::{NodeBuilder, WitValueBuilderExtensions};
76
77#[cfg(any(feature = "host", feature = "stub"))]
78pub use extractor::{WitNodePointer, WitValueExtractor};
79
80#[cfg(not(feature = "host"))]
81#[cfg(feature = "stub")]
82pub use bindings::wasi;
83
84#[cfg(not(feature = "host"))]
85#[cfg(feature = "stub")]
86pub use bindings::golem::rpc0_2_2 as golem_rpc_0_2_x;
87
88#[cfg(not(feature = "host"))]
89#[cfg(feature = "stub")]
90pub use golem_rpc_0_2_x::types::{
91    AgentId, ComponentId, FutureInvokeResult, NodeIndex, ResourceMode, RpcError, Uri, Uuid,
92    WasmRpc, WitNode, WitType, WitTypeNode, WitValue,
93};
94
95#[cfg(not(feature = "host"))]
96#[cfg(feature = "stub")]
97pub use bindings::wasi::io::poll::Pollable;
98
99#[cfg(feature = "host")]
100pub use wasmtime_wasi::p2::DynPollable;
101
102#[cfg(feature = "host")]
103mod generated {
104    use ::wasmtime::component::bindgen;
105    bindgen!({
106        path: "wit",
107        world: "wasm-rpc",
108        tracing: false,
109        async: true,
110        trappable_imports: true,
111        with: {
112            "golem:rpc/types/wasm-rpc": super::WasmRpcEntry,
113            "golem:rpc/types/future-invoke-result": super::FutureInvokeResultEntry,
114            "golem:rpc/types/cancellation-token": super::CancellationTokenEntry,
115            "wasi:io/poll/pollable": super::DynPollable,
116        },
117        wasmtime_crate: ::wasmtime,
118    });
119}
120
121#[cfg(feature = "host")]
122pub use generated::wasi;
123
124#[cfg(feature = "host")]
125pub use generated::golem::rpc0_2_2 as golem_rpc_0_2_x;
126
127#[cfg(feature = "host")]
128pub use golem_rpc_0_2_x::types::{
129    AgentId, ComponentId, Host, HostWasmRpc, NodeIndex, ResourceMode, RpcError, Uri, Uuid, WitNode,
130    WitType, WitTypeNode, WitValue,
131};
132
133use std::fmt;
134use std::fmt::{Display, Formatter};
135use std::str::FromStr;
136
137impl From<wasi::clocks::wall_clock::Datetime> for DateTime<Utc> {
138    fn from(value: wasi::clocks::wall_clock::Datetime) -> DateTime<Utc> {
139        DateTime::from_timestamp(value.seconds as i64, value.nanoseconds)
140            .expect("Received invalid datetime from wasi")
141    }
142}
143
144impl From<Uuid> for uuid::Uuid {
145    fn from(value: Uuid) -> Self {
146        uuid::Uuid::from_u64_pair(value.high_bits, value.low_bits)
147    }
148}
149
150impl From<uuid::Uuid> for Uuid {
151    fn from(uuid: uuid::Uuid) -> Self {
152        let (high_bits, low_bits) = uuid.as_u64_pair();
153        Uuid {
154            high_bits,
155            low_bits,
156        }
157    }
158}
159
160#[cfg(feature = "host")]
161pub struct WasmRpcEntry {
162    pub payload: Box<dyn std::any::Any + Send + Sync>,
163}
164
165#[cfg(feature = "host")]
166#[async_trait::async_trait]
167pub trait SubscribeAny: std::any::Any {
168    async fn ready(&mut self);
169    fn as_any(&self) -> &dyn std::any::Any;
170    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
171}
172
173#[cfg(feature = "host")]
174pub struct FutureInvokeResultEntry {
175    pub payload: Box<dyn SubscribeAny + Send + Sync>,
176}
177
178#[cfg(feature = "host")]
179#[async_trait::async_trait]
180impl wasmtime_wasi::p2::Pollable for FutureInvokeResultEntry {
181    async fn ready(&mut self) {
182        self.payload.ready().await
183    }
184}
185
186#[cfg(feature = "host")]
187pub struct CancellationTokenEntry {
188    pub schedule_id: Vec<u8>, // ScheduleId is defined locally in the worker-executor, so store a serialized version here
189}
190
191#[cfg(feature = "host")]
192pub use text::{parse_value_and_type, print_value_and_type};
193
194pub use value_and_type::*;
195
196#[cfg(feature = "host")]
197impl arbitrary::Arbitrary<'_> for Uri {
198    fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
199        let uri = u.arbitrary::<String>()?;
200        Ok(Uri { value: uri })
201    }
202}
203
204#[cfg(feature = "host")]
205impl PartialEq for Uri {
206    fn eq(&self, other: &Self) -> bool {
207        self.value == other.value
208    }
209}
210
211/// A tree representation of Value - isomorphic to the protobuf Val type but easier to work with in Rust
212#[derive(Debug, Clone, PartialEq)]
213#[cfg_attr(feature = "host", derive(arbitrary::Arbitrary))]
214#[cfg_attr(feature = "host", derive(::bincode::Encode, ::bincode::Decode))]
215pub enum Value {
216    Bool(bool),
217    U8(u8),
218    U16(u16),
219    U32(u32),
220    U64(u64),
221    S8(i8),
222    S16(i16),
223    S32(i32),
224    S64(i64),
225    F32(f32),
226    F64(f64),
227    Char(char),
228    String(String),
229    List(Vec<Value>),
230    Tuple(Vec<Value>),
231    Record(Vec<Value>),
232    Variant {
233        case_idx: u32,
234        case_value: Option<Box<Value>>,
235    },
236    Enum(u32),
237    Flags(Vec<bool>),
238    Option(Option<Box<Value>>),
239    Result(Result<Option<Box<Value>>, Option<Box<Value>>>),
240    Handle {
241        uri: String,
242        resource_id: u64,
243    },
244}
245
246#[cfg(any(feature = "host", feature = "stub"))]
247impl From<Value> for WitValue {
248    fn from(value: Value) -> Self {
249        let mut builder = WitValueBuilder::new();
250        build_wit_value(value, &mut builder);
251        builder.build()
252    }
253}
254
255#[cfg(any(feature = "host", feature = "stub"))]
256impl PartialEq for WitValue {
257    fn eq(&self, other: &Self) -> bool {
258        let a: Value = self.clone().into();
259        let b: Value = other.clone().into();
260        a == b
261    }
262}
263
264#[cfg(any(feature = "host", feature = "stub"))]
265fn build_wit_value(value: Value, builder: &mut WitValueBuilder) -> NodeIndex {
266    match value {
267        Value::Bool(value) => builder.add_bool(value),
268        Value::U8(value) => builder.add_u8(value),
269        Value::U16(value) => builder.add_u16(value),
270        Value::U32(value) => builder.add_u32(value),
271        Value::U64(value) => builder.add_u64(value),
272        Value::S8(value) => builder.add_s8(value),
273        Value::S16(value) => builder.add_s16(value),
274        Value::S32(value) => builder.add_s32(value),
275        Value::S64(value) => builder.add_s64(value),
276        Value::F32(value) => builder.add_f32(value),
277        Value::F64(value) => builder.add_f64(value),
278        Value::Char(value) => builder.add_char(value),
279        Value::String(value) => builder.add_string(&value),
280        Value::List(values) => {
281            let list_idx = builder.add_list();
282            let mut items = Vec::new();
283            for value in values {
284                let item_idx = build_wit_value(value, builder);
285                items.push(item_idx);
286            }
287            builder.finish_seq(items, list_idx);
288            list_idx
289        }
290        Value::Tuple(values) => {
291            let tuple_idx = builder.add_tuple();
292            let mut items = Vec::new();
293            for value in values {
294                let item_idx = build_wit_value(value, builder);
295                items.push(item_idx);
296            }
297            builder.finish_seq(items, tuple_idx);
298            tuple_idx
299        }
300        Value::Record(fields) => {
301            let record_idx = builder.add_record();
302            let mut items = Vec::new();
303            for value in fields {
304                let item_idx = build_wit_value(value, builder);
305                items.push(item_idx);
306            }
307            builder.finish_seq(items, record_idx);
308            record_idx
309        }
310        Value::Variant {
311            case_idx,
312            case_value: Some(case_value),
313        } => {
314            let variant_idx = builder.add_variant(case_idx, -1);
315            let inner_idx = build_wit_value(*case_value, builder);
316            builder.finish_child(inner_idx, variant_idx);
317            variant_idx
318        }
319        Value::Variant {
320            case_idx,
321            case_value: None,
322        } => builder.add_variant_unit(case_idx),
323        Value::Enum(value) => builder.add_enum_value(value),
324        Value::Flags(values) => builder.add_flags(values),
325        Value::Option(value) => {
326            if let Some(value) = value {
327                let option_idx = builder.add_option_some();
328                let inner_idx = build_wit_value(*value, builder);
329                builder.finish_child(inner_idx, option_idx);
330                option_idx
331            } else {
332                builder.add_option_none()
333            }
334        }
335        Value::Result(result) => match result {
336            Ok(Some(ok)) => {
337                let result_idx = builder.add_result_ok();
338                let inner_idx = build_wit_value(*ok, builder);
339                builder.finish_child(inner_idx, result_idx);
340                result_idx
341            }
342            Ok(None) => builder.add_result_ok_unit(),
343            Err(Some(err)) => {
344                let result_idx = builder.add_result_err();
345                let inner_idx = build_wit_value(*err, builder);
346                builder.finish_child(inner_idx, result_idx);
347                result_idx
348            }
349            Err(None) => builder.add_result_err_unit(),
350        },
351        Value::Handle { uri, resource_id } => builder.add_handle(Uri { value: uri }, resource_id),
352    }
353}
354
355impl Value {
356    pub fn type_case_name(&self) -> &'static str {
357        match self {
358            Value::Bool(_) => "bool",
359            Value::U8(_) => "u8",
360            Value::U16(_) => "u16",
361            Value::U32(_) => "u32",
362            Value::U64(_) => "u64",
363            Value::S8(_) => "s8",
364            Value::S16(_) => "s16",
365            Value::S32(_) => "s32",
366            Value::S64(_) => "s64",
367            Value::F32(_) => "f32",
368            Value::F64(_) => "f64",
369            Value::Char(_) => "char",
370            Value::String(_) => "string",
371            Value::List(_) => "list",
372            Value::Tuple(_) => "tuple",
373            Value::Record(_) => "record",
374            Value::Variant { .. } => "variant",
375            Value::Enum(_) => "enum",
376            Value::Flags(_) => "flags",
377            Value::Option(_) => "option",
378            Value::Result(_) => "result",
379            Value::Handle { .. } => "handle",
380        }
381    }
382}
383
384#[cfg(any(feature = "host", feature = "stub"))]
385impl From<WitValue> for Value {
386    fn from(value: WitValue) -> Self {
387        assert!(!value.nodes.is_empty());
388        build_tree(&value.nodes[0], &value.nodes)
389    }
390}
391
392#[cfg(any(feature = "host", feature = "stub"))]
393fn build_tree(node: &WitNode, nodes: &[WitNode]) -> Value {
394    match node {
395        WitNode::RecordValue(field_indices) => {
396            let mut fields = Vec::new();
397            for index in field_indices {
398                let value = build_tree(&nodes[*index as usize], nodes);
399                fields.push(value);
400            }
401            Value::Record(fields)
402        }
403        WitNode::VariantValue((case_idx, Some(inner_idx))) => {
404            let value = build_tree(&nodes[*inner_idx as usize], nodes);
405            Value::Variant {
406                case_idx: *case_idx,
407                case_value: Some(Box::new(value)),
408            }
409        }
410        WitNode::VariantValue((case_idx, None)) => Value::Variant {
411            case_idx: *case_idx,
412            case_value: None,
413        },
414        WitNode::EnumValue(value) => Value::Enum(*value),
415        WitNode::FlagsValue(values) => Value::Flags(values.clone()),
416        WitNode::TupleValue(indices) => {
417            let mut values = Vec::new();
418            for index in indices {
419                let value = build_tree(&nodes[*index as usize], nodes);
420                values.push(value);
421            }
422            Value::Tuple(values)
423        }
424        WitNode::ListValue(indices) => {
425            let mut values = Vec::new();
426            for index in indices {
427                let value = build_tree(&nodes[*index as usize], nodes);
428                values.push(value);
429            }
430            Value::List(values)
431        }
432        WitNode::OptionValue(Some(index)) => {
433            let value = build_tree(&nodes[*index as usize], nodes);
434            Value::Option(Some(Box::new(value)))
435        }
436        WitNode::OptionValue(None) => Value::Option(None),
437        WitNode::ResultValue(Ok(Some(index))) => {
438            let value = build_tree(&nodes[*index as usize], nodes);
439            Value::Result(Ok(Some(Box::new(value))))
440        }
441        WitNode::ResultValue(Ok(None)) => Value::Result(Ok(None)),
442        WitNode::ResultValue(Err(Some(index))) => {
443            let value = build_tree(&nodes[*index as usize], nodes);
444            Value::Result(Err(Some(Box::new(value))))
445        }
446        WitNode::ResultValue(Err(None)) => Value::Result(Err(None)),
447        WitNode::PrimU8(value) => Value::U8(*value),
448        WitNode::PrimU16(value) => Value::U16(*value),
449        WitNode::PrimU32(value) => Value::U32(*value),
450        WitNode::PrimU64(value) => Value::U64(*value),
451        WitNode::PrimS8(value) => Value::S8(*value),
452        WitNode::PrimS16(value) => Value::S16(*value),
453        WitNode::PrimS32(value) => Value::S32(*value),
454        WitNode::PrimS64(value) => Value::S64(*value),
455        WitNode::PrimFloat32(value) => Value::F32(*value),
456        WitNode::PrimFloat64(value) => Value::F64(*value),
457        WitNode::PrimChar(value) => Value::Char(*value),
458        WitNode::PrimBool(value) => Value::Bool(*value),
459        WitNode::PrimString(value) => Value::String(value.clone()),
460        WitNode::Handle((uri, value)) => Value::Handle {
461            uri: uri.value.clone(),
462            resource_id: *value,
463        },
464    }
465}
466
467#[cfg(feature = "host")]
468impl<'a> arbitrary::Arbitrary<'a> for WitValue {
469    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
470        let arbitrary_value = u.arbitrary::<Value>()?;
471        Ok(arbitrary_value.into())
472    }
473}
474
475impl From<uuid::Uuid> for ComponentId {
476    fn from(value: uuid::Uuid) -> Self {
477        Self { uuid: value.into() }
478    }
479}
480
481impl From<ComponentId> for uuid::Uuid {
482    fn from(value: ComponentId) -> Self {
483        value.uuid.into()
484    }
485}
486
487impl FromStr for ComponentId {
488    type Err = uuid::Error;
489
490    fn from_str(s: &str) -> Result<Self, Self::Err> {
491        Ok(uuid::Uuid::parse_str(s)?.into())
492    }
493}
494
495impl Display for ComponentId {
496    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
497        let uuid: uuid::Uuid = self.uuid.into();
498        write!(f, "{uuid}")
499    }
500}
501
502impl Display for AgentId {
503    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
504        write!(f, "{}/{}", self.component_id, self.agent_id)
505    }
506}
507
508impl FromStr for AgentId {
509    type Err = String;
510
511    fn from_str(s: &str) -> Result<Self, Self::Err> {
512        let parts: Vec<&str> = s.split('/').collect();
513        if parts.len() == 2 {
514            let component_id = ComponentId::from_str(parts[0])
515                .map_err(|_| format!("invalid component id: {s} - expected uuid"))?;
516            let agent_id = parts[1].to_string();
517            Ok(Self {
518                component_id,
519                agent_id,
520            })
521        } else {
522            Err(format!(
523                "invalid agent id: {s} - expected format: <component_id>/<agent_id>"
524            ))
525        }
526    }
527}
528
529impl TryFrom<Uri> for AgentId {
530    type Error = String;
531
532    fn try_from(uri: Uri) -> Result<Self, Self::Error> {
533        let urn = uri.value;
534        if !urn.starts_with("urn:worker:") {
535            Err("Invalid URN: must start with 'urn:worker:', got '{urn}'".to_string())
536        } else {
537            let remaining = &urn[11..];
538            let parts: Vec<&str> = remaining.split('/').collect();
539            match parts.len() {
540                2 => {
541                    let component_id = ComponentId::from_str(parts[0]).map_err(|err|
542                        format!("Invalid URN: expected UUID for component_id: {err}")
543                    )?;
544                    let agent_id = parts[1];
545                    Ok(AgentId {
546                        component_id,
547                        agent_id: agent_id.to_string(),
548                    })
549                }
550                _ => Err(format!(
551                    "Invalid URN: expected format 'urn:worker:<component_id>/<worker_name>', got '{urn}'",
552                )),
553            }
554        }
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use test_r::test;
561
562    use crate::{Value, WitValue};
563    use proptest::prelude::*;
564    use proptest_arbitrary_interop::arb_sized;
565
566    const CASES: u32 = 10000;
567    const SIZE: usize = 4096;
568
569    proptest! {
570
571        #![proptest_config(ProptestConfig {
572            cases: CASES, .. ProptestConfig::default()
573        })]
574        #[test]
575        fn round_trip(value in arb_sized::<Value>(SIZE).prop_filter("Value must be equal to itself", |v| v.eq(v))) {
576            let wit_value: WitValue = value.clone().into();
577            let round_trip_value: Value = wit_value.into();
578            prop_assert_eq!(value, round_trip_value);
579        }
580    }
581}