1#![allow(clippy::needless_lifetimes)]
2use crate::call::{AsyncCaller, SyncCaller};
3use candid::utils::ArgumentEncoder;
4use candid::{ser::IDLBuilder, types::value::IDLValue, utils::ArgumentDecoder, CandidType, Encode};
5use ic_agent::{export::Principal, Agent, AgentError, RequestId};
6use std::convert::TryInto;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum CanisterBuilderError {
12 #[error("Getting the Canister ID returned an error: {0}")]
14 PrincipalError(#[from] Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>),
15
16 #[error("Must specify an Agent")]
18 MustSpecifyAnAgent(),
19
20 #[error("Must specify a Canister ID")]
22 MustSpecifyCanisterId(),
23}
24
25#[derive(Debug, Default)]
27pub struct CanisterBuilder<'agent> {
28 agent: Option<&'agent Agent>,
29 canister_id: Option<Result<Principal, CanisterBuilderError>>,
30}
31
32impl<'agent> CanisterBuilder<'agent> {
33 pub fn new() -> CanisterBuilder<'static> {
35 Default::default()
36 }
37
38 pub fn with_canister_id<E, P>(self, canister_id: P) -> Self
40 where
41 E: 'static + std::error::Error + std::marker::Send + std::marker::Sync,
42 P: TryInto<Principal, Error = E>,
43 {
44 Self {
45 canister_id: Some(
46 canister_id
47 .try_into()
48 .map_err(|e| CanisterBuilderError::PrincipalError(Box::new(e))),
49 ),
50 ..self
51 }
52 }
53
54 pub fn with_agent(self, agent: &'agent Agent) -> Self {
56 CanisterBuilder {
57 agent: Some(agent),
58 ..self
59 }
60 }
61
62 pub fn build(self) -> Result<Canister<'agent>, CanisterBuilderError> {
64 let canister_id = if let Some(cid) = self.canister_id {
65 cid?
66 } else {
67 return Err(CanisterBuilderError::MustSpecifyCanisterId());
68 };
69
70 let agent = self
71 .agent
72 .ok_or(CanisterBuilderError::MustSpecifyAnAgent())?;
73 Ok(Canister { agent, canister_id })
74 }
75}
76
77#[derive(Debug, Clone)]
84pub struct Canister<'agent> {
85 pub(super) agent: &'agent Agent,
86 pub(super) canister_id: Principal,
87}
88
89impl<'agent> Canister<'agent> {
90 pub fn canister_id_(&self) -> &Principal {
93 &self.canister_id
94 }
95
96 pub fn canister_id(&self) -> &Principal {
98 &self.canister_id
99 }
100
101 pub fn update_<'canister>(
104 &'canister self,
105 method_name: &str,
106 ) -> AsyncCallBuilder<'agent, 'canister> {
107 AsyncCallBuilder::new(self, method_name)
108 }
109
110 pub fn update<'canister>(
112 &'canister self,
113 method_name: &str,
114 ) -> AsyncCallBuilder<'agent, 'canister> {
115 AsyncCallBuilder::new(self, method_name)
116 }
117
118 pub fn query_<'canister>(
121 &'canister self,
122 method_name: &str,
123 ) -> SyncCallBuilder<'agent, 'canister> {
124 SyncCallBuilder::new(self, method_name)
125 }
126
127 pub fn query<'canister>(
129 &'canister self,
130 method_name: &str,
131 ) -> SyncCallBuilder<'agent, 'canister> {
132 SyncCallBuilder::new(self, method_name)
133 }
134
135 pub async fn wait<'canister>(
137 &'canister self,
138 request_id: &RequestId,
139 ) -> Result<Vec<u8>, AgentError> {
140 self.agent
141 .wait(request_id, self.canister_id)
142 .await
143 .map(|x| x.0)
144 }
145
146 pub fn clone_with_(&self, id: Principal) -> Self {
149 Self {
150 agent: self.agent,
151 canister_id: id,
152 }
153 }
154 pub fn clone_with(&self, id: Principal) -> Self {
156 Self {
157 agent: self.agent,
158 canister_id: id,
159 }
160 }
161
162 pub fn builder() -> CanisterBuilder<'agent> {
164 Default::default()
165 }
166}
167
168#[derive(Debug, Default)]
170pub struct Argument(pub(crate) Option<Result<Vec<u8>, AgentError>>);
171
172impl Argument {
173 pub fn set_idl_arg<A: CandidType>(&mut self, arg: A) {
175 match self.0 {
176 None => self.0 = Some(Encode!(&arg).map_err(|e| e.into())),
177 Some(_) => panic!("argument is being set more than once"),
178 }
179 }
180
181 pub fn set_value_arg(&mut self, arg: IDLValue) {
183 match self.0 {
184 None => {
185 let mut builder = IDLBuilder::new();
186 let result = builder
187 .value_arg(&arg)
188 .and_then(|builder| builder.serialize_to_vec())
189 .map_err(Into::into);
190 self.0 = Some(result);
191 }
192 Some(_) => panic!("argument is being set more than once"),
193 }
194 }
195
196 pub fn set_raw_arg(&mut self, arg: Vec<u8>) {
198 match self.0 {
199 None => self.0 = Some(Ok(arg)),
200 Some(_) => panic!("argument is being set more than once"),
201 }
202 }
203
204 pub fn serialize(self) -> Result<Vec<u8>, AgentError> {
206 self.0.unwrap_or_else(|| Ok(Encode!()?))
207 }
208
209 pub fn reset(&mut self) {
211 *self = Default::default();
212 }
213
214 pub fn new() -> Self {
216 Default::default()
217 }
218
219 pub fn from_raw(raw: Vec<u8>) -> Self {
221 Self(Some(Ok(raw)))
222 }
223
224 pub fn from_candid(tuple: impl ArgumentEncoder) -> Self {
226 let mut builder = IDLBuilder::new();
227 let result = tuple
228 .encode(&mut builder)
229 .and_then(|_| builder.serialize_to_vec())
230 .map_err(Into::into);
231 Self(Some(result))
232 }
233}
234
235#[derive(Debug)]
239pub struct SyncCallBuilder<'agent, 'canister> {
240 canister: &'canister Canister<'agent>,
241 method_name: String,
242 effective_canister_id: Principal,
243 arg: Argument,
244}
245
246impl<'agent: 'canister, 'canister> SyncCallBuilder<'agent, 'canister> {
247 pub(super) fn new<M: Into<String>>(
249 canister: &'canister Canister<'agent>,
250 method_name: M,
251 ) -> Self {
252 Self {
253 canister,
254 method_name: method_name.into(),
255 effective_canister_id: canister.canister_id().to_owned(),
256 arg: Default::default(),
257 }
258 }
259}
260
261impl<'agent: 'canister, 'canister> SyncCallBuilder<'agent, 'canister> {
262 pub fn with_arg<Argument>(mut self, arg: Argument) -> Self
264 where
265 Argument: CandidType + Sync + Send,
266 {
267 self.arg.set_idl_arg(arg);
268 self
269 }
270 pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
272 assert!(self.arg.0.is_none(), "argument is being set more than once");
273 self.arg = Argument::from_candid(tuple);
274 self
275 }
276
277 pub fn with_value_arg(mut self, arg: IDLValue) -> Self {
281 self.arg.set_value_arg(arg);
282 self
283 }
284
285 pub fn with_arg_raw(mut self, arg: Vec<u8>) -> Self {
287 self.arg.set_raw_arg(arg);
288 self
289 }
290
291 pub fn with_effective_canister_id(mut self, canister_id: Principal) -> Self {
293 self.effective_canister_id = canister_id;
294 self
295 }
296
297 pub fn build<Output>(self) -> SyncCaller<'agent, Output>
299 where
300 Output: for<'de> ArgumentDecoder<'de> + Send + Sync,
301 {
302 let c = self.canister;
303 SyncCaller {
304 agent: c.agent,
305 effective_canister_id: self.effective_canister_id,
306 canister_id: c.canister_id,
307 method_name: self.method_name.clone(),
308 arg: self.arg.serialize(),
309 expiry: Default::default(),
310 phantom_out: std::marker::PhantomData,
311 }
312 }
313}
314
315#[derive(Debug)]
319pub struct AsyncCallBuilder<'agent, 'canister> {
320 canister: &'canister Canister<'agent>,
321 method_name: String,
322 effective_canister_id: Principal,
323 arg: Argument,
324}
325
326impl<'agent: 'canister, 'canister> AsyncCallBuilder<'agent, 'canister> {
327 pub(super) fn new(
329 canister: &'canister Canister<'agent>,
330 method_name: &str,
331 ) -> AsyncCallBuilder<'agent, 'canister> {
332 Self {
333 canister,
334 method_name: method_name.to_string(),
335 effective_canister_id: canister.canister_id().to_owned(),
336 arg: Default::default(),
337 }
338 }
339}
340
341impl<'agent: 'canister, 'canister> AsyncCallBuilder<'agent, 'canister> {
342 pub fn with_arg<Argument>(mut self, arg: Argument) -> Self
344 where
345 Argument: CandidType + Sync + Send,
346 {
347 self.arg.set_idl_arg(arg);
348 self
349 }
350 pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
352 assert!(self.arg.0.is_none(), "argument is being set more than once");
353 self.arg = Argument::from_candid(tuple);
354 self
355 }
356
357 pub fn with_arg_raw(mut self, arg: Vec<u8>) -> Self {
359 self.arg.set_raw_arg(arg);
360 self
361 }
362
363 pub fn with_effective_canister_id(mut self, canister_id: Principal) -> Self {
365 self.effective_canister_id = canister_id;
366 self
367 }
368
369 pub fn build<Output>(self) -> AsyncCaller<'agent, Output>
371 where
372 Output: for<'de> ArgumentDecoder<'de> + Send + Sync,
373 {
374 let c = self.canister;
375 AsyncCaller {
376 agent: c.agent,
377 effective_canister_id: self.effective_canister_id,
378 canister_id: c.canister_id,
379 method_name: self.method_name.clone(),
380 arg: self.arg.serialize(),
381 expiry: Default::default(),
382 phantom_out: std::marker::PhantomData,
383 }
384 }
385}
386
387#[cfg(all(test, unix))] mod tests {
389 use super::*;
390 use crate::call::AsyncCall;
391 use crate::interfaces::ManagementCanister;
392
393 #[tokio::test]
394 async fn simple() {
395 ref_tests::utils::with_agent(async move |pic, agent| {
396 let management_canister = ManagementCanister::from_canister(
397 Canister::builder()
398 .with_agent(&agent)
399 .with_canister_id("aaaaa-aa")
400 .build()
401 .unwrap(),
402 );
403
404 let (new_canister_id,) = management_canister
405 .create_canister()
406 .as_provisional_create_with_amount(None)
407 .with_effective_canister_id(ref_tests::utils::get_effective_canister_id(pic).await)
408 .call_and_wait()
409 .await
410 .unwrap();
411
412 let (status,) = management_canister
413 .canister_status(&new_canister_id)
414 .call_and_wait()
415 .await
416 .unwrap();
417
418 assert_eq!(format!("{:?}", status.status), "Running");
419
420 let canister_wasm = b"\0asm\x01\0\0\0";
421 management_canister
422 .install_code(&new_canister_id, canister_wasm)
423 .call_and_wait()
424 .await
425 .unwrap();
426
427 let canister = Canister::builder()
428 .with_agent(&agent)
429 .with_canister_id(new_canister_id)
430 .build()
431 .unwrap();
432
433 assert!(canister
434 .update("hello")
435 .build::<()>()
436 .call_and_wait()
437 .await
438 .is_err());
439 Ok(())
440 })
441 .await
442 }
443}