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