Skip to main content

orleans_rust_client/
grain.rs

1//! Grain references and method invocation.
2
3use std::time::Duration;
4
5use crate::client::{OrleansClient, RawResponse};
6use crate::error::OrleansError;
7use crate::key::GrainKey;
8use crate::request_context::RequestContext;
9
10/// A handle to a specific grain (interface + grain type + key) on which methods
11/// can be invoked.
12///
13/// `GrainRef` is cheap to clone and carries optional per-grain overrides for
14/// request context and timeout.
15#[derive(Clone)]
16pub struct GrainRef {
17    client: OrleansClient,
18    interface_name: String,
19    grain_type: String,
20    key: GrainKey,
21    context: RequestContext,
22    timeout: Option<Duration>,
23}
24
25impl GrainRef {
26    pub(crate) fn new(
27        client: OrleansClient,
28        interface_name: String,
29        grain_type: String,
30        key: GrainKey,
31    ) -> Self {
32        Self {
33            client,
34            interface_name,
35            grain_type,
36            key,
37            context: RequestContext::new(),
38            timeout: None,
39        }
40    }
41
42    /// The grain interface name this reference targets.
43    #[must_use]
44    pub fn interface_name(&self) -> &str {
45        &self.interface_name
46    }
47
48    /// The grain type alias this reference targets.
49    #[must_use]
50    pub fn grain_type(&self) -> &str {
51        &self.grain_type
52    }
53
54    /// The key this reference targets.
55    #[must_use]
56    pub fn key(&self) -> &GrainKey {
57        &self.key
58    }
59
60    /// Return a copy of this reference with request-context entries that are
61    /// applied to every call made through it.
62    #[must_use]
63    pub fn with_context(mut self, context: RequestContext) -> Self {
64        self.context = context;
65        self
66    }
67
68    /// Return a copy of this reference with a per-call deadline override.
69    #[must_use]
70    pub fn with_timeout(mut self, timeout: Duration) -> Self {
71        self.timeout = Some(timeout);
72        self
73    }
74
75    fn effective_context(&self) -> RequestContext {
76        self.client
77            .config()
78            .default_context
79            .merged_with(&self.context)
80    }
81
82    /// Invoke `method` with an opaque, already-encoded payload under `codec`.
83    ///
84    /// This is the lowest-level entry point; prefer [`GrainRef::invoke_json`]
85    /// for typical use.
86    ///
87    /// # Errors
88    /// Returns an [`OrleansError`] on transport failure, timeout, or a
89    /// structured bridge error.
90    pub async fn invoke(
91        &self,
92        method: &str,
93        payload: Vec<u8>,
94        codec: &str,
95    ) -> Result<RawResponse, OrleansError> {
96        let context = self.effective_context();
97        self.client
98            .invoke_raw(crate::client::InvokeCall {
99                interface_name: &self.interface_name,
100                grain_type: &self.grain_type,
101                key: &self.key,
102                method,
103                payload,
104                codec,
105                context: &context,
106                timeout: self.timeout,
107            })
108            .await
109    }
110
111    /// Invoke `method`, serializing `request` to JSON and deserializing the
112    /// JSON response.
113    ///
114    /// Use a unit value (`&()`) for methods that take no argument.
115    ///
116    /// # Errors
117    /// Returns [`OrleansError::Serialization`] if (de)serialization fails, or a
118    /// transport/bridge error otherwise.
119    #[cfg(feature = "json")]
120    pub async fn invoke_json<Req, Resp>(
121        &self,
122        method: &str,
123        request: &Req,
124    ) -> Result<Resp, OrleansError>
125    where
126        Req: serde::Serialize + ?Sized,
127        Resp: serde::de::DeserializeOwned,
128    {
129        Ok(self.invoke_json_with_context(method, request).await?.0)
130    }
131
132    /// Like [`GrainRef::invoke_json`], but also returns the response-context
133    /// entries the grain produced.
134    ///
135    /// # Errors
136    /// See [`GrainRef::invoke_json`].
137    #[cfg(feature = "json")]
138    pub async fn invoke_json_with_context<Req, Resp>(
139        &self,
140        method: &str,
141        request: &Req,
142    ) -> Result<(Resp, std::collections::HashMap<String, String>), OrleansError>
143    where
144        Req: serde::Serialize + ?Sized,
145        Resp: serde::de::DeserializeOwned,
146    {
147        let payload =
148            serde_json::to_vec(request).map_err(|e| OrleansError::Serialization(e.to_string()))?;
149        let response = self.invoke(method, payload, "json").await?;
150        let value = serde_json::from_slice(&response.payload)
151            .map_err(|e| OrleansError::Serialization(e.to_string()))?;
152        Ok((value, response.response_context))
153    }
154
155    /// Invoke `method` with a protobuf-encoded request and response.
156    ///
157    /// The payload bytes are opaque to the bridge transport; the bridge's grain
158    /// invoker is responsible for decoding them.
159    ///
160    /// # Errors
161    /// Returns [`OrleansError::Serialization`] on decode failure, or a
162    /// transport/bridge error otherwise.
163    #[cfg(feature = "protobuf")]
164    pub async fn invoke_protobuf<Req, Resp>(
165        &self,
166        method: &str,
167        request: &Req,
168    ) -> Result<Resp, OrleansError>
169    where
170        Req: prost::Message,
171        Resp: prost::Message + Default,
172    {
173        let payload = request.encode_to_vec();
174        let response = self.invoke(method, payload, "protobuf").await?;
175        Resp::decode(response.payload.as_slice())
176            .map_err(|e| OrleansError::Serialization(e.to_string()))
177    }
178}