ic_utils/interfaces/management_canister/
builders.rs

1//! Builder interfaces for some method calls of the management canister.
2
3#[doc(inline)]
4pub use super::attributes::{
5    ComputeAllocation, FreezingThreshold, MemoryAllocation, ReservedCyclesLimit, WasmMemoryLimit,
6};
7use super::{ChunkHash, LogVisibility, ManagementCanister};
8use crate::call::CallFuture;
9use crate::{
10    call::AsyncCall, canister::Argument, interfaces::management_canister::MgmtMethod, Canister,
11};
12use async_trait::async_trait;
13use candid::{utils::ArgumentEncoder, CandidType, Deserialize, Nat};
14use futures_util::{
15    future::ready,
16    stream::{self, FuturesUnordered},
17    FutureExt, Stream, StreamExt, TryStreamExt,
18};
19use ic_agent::{agent::CallResponse, export::Principal, AgentError};
20pub use ic_management_canister_types::{
21    CanisterInstallMode, CanisterSettings, EnvironmentVariable, InstallCodeArgs, UpgradeFlags,
22    WasmMemoryPersistence,
23};
24use sha2::{Digest, Sha256};
25use std::{
26    collections::BTreeSet,
27    convert::{From, TryInto},
28    future::IntoFuture,
29    pin::Pin,
30};
31
32/// A builder for a `create_canister` call.
33#[derive(Debug)]
34pub struct CreateCanisterBuilder<'agent, 'canister: 'agent> {
35    canister: &'canister Canister<'agent>,
36    effective_canister_id: Principal,
37    controllers: Option<Result<Vec<Principal>, AgentError>>,
38    compute_allocation: Option<Result<ComputeAllocation, AgentError>>,
39    memory_allocation: Option<Result<MemoryAllocation, AgentError>>,
40    freezing_threshold: Option<Result<FreezingThreshold, AgentError>>,
41    reserved_cycles_limit: Option<Result<ReservedCyclesLimit, AgentError>>,
42    wasm_memory_limit: Option<Result<WasmMemoryLimit, AgentError>>,
43    wasm_memory_threshold: Option<Result<WasmMemoryLimit, AgentError>>,
44    log_visibility: Option<Result<LogVisibility, AgentError>>,
45    environment_variables: Option<Result<Vec<EnvironmentVariable>, AgentError>>,
46    is_provisional_create: bool,
47    amount: Option<u128>,
48    specified_id: Option<Principal>,
49}
50
51impl<'agent, 'canister: 'agent> CreateCanisterBuilder<'agent, 'canister> {
52    /// Create an `CreateCanister` builder, which is also an `AsyncCall` implementation.
53    pub fn builder(canister: &'canister Canister<'agent>) -> Self {
54        Self {
55            canister,
56            effective_canister_id: Principal::management_canister(),
57            controllers: None,
58            compute_allocation: None,
59            memory_allocation: None,
60            freezing_threshold: None,
61            reserved_cycles_limit: None,
62            wasm_memory_limit: None,
63            wasm_memory_threshold: None,
64            log_visibility: None,
65            environment_variables: None,
66            is_provisional_create: false,
67            amount: None,
68            specified_id: None,
69        }
70    }
71
72    /// Until developers can convert real ICP tokens to provision a new canister with cycles,
73    /// the system provides the `provisional_create_canister_with_cycles` method.
74    /// It behaves as `create_canister`, but initializes the canister’s balance with amount fresh cycles
75    /// (using `MAX_CANISTER_BALANCE` if amount = null, else capping the balance at `MAX_CANISTER_BALANCE`).
76    /// Cycles added to this call via `ic0.call_cycles_add` are returned to the caller.
77    /// This method is only available in local development instances, and will be removed in the future.
78    #[allow(clippy::wrong_self_convention)]
79    pub fn as_provisional_create_with_amount(self, amount: Option<u128>) -> Self {
80        Self {
81            is_provisional_create: true,
82            amount,
83            ..self
84        }
85    }
86
87    /// Specify the canister id.
88    ///
89    /// The `effective_canister_id` will also be set with the same value so that ic-ref can determine
90    /// the target subnet of this request. The replica implementation ignores it.
91    pub fn as_provisional_create_with_specified_id(self, specified_id: Principal) -> Self {
92        Self {
93            is_provisional_create: true,
94            specified_id: Some(specified_id),
95            effective_canister_id: specified_id,
96            ..self
97        }
98    }
99
100    /// Pass in an effective canister id for the update call.
101    pub fn with_effective_canister_id<C, E>(self, effective_canister_id: C) -> Self
102    where
103        E: std::fmt::Display,
104        C: TryInto<Principal, Error = E>,
105    {
106        match effective_canister_id.try_into() {
107            Ok(effective_canister_id) => Self {
108                effective_canister_id,
109                ..self
110            },
111            Err(_) => self,
112        }
113    }
114
115    /// Pass in an optional controller for the canister. If this is [`None`],
116    /// it will revert the controller to default.
117    pub fn with_optional_controller<C, E>(self, controller: Option<C>) -> Self
118    where
119        E: std::fmt::Display,
120        C: TryInto<Principal, Error = E>,
121    {
122        let controller_to_add: Option<Result<Principal, _>> = controller.map(|ca| {
123            ca.try_into()
124                .map_err(|e| AgentError::MessageError(format!("{e}")))
125        });
126        let controllers: Option<Result<Vec<Principal>, _>> =
127            match (controller_to_add, self.controllers) {
128                (_, Some(Err(sticky))) => Some(Err(sticky)),
129                (Some(Err(e)), _) => Some(Err(e)),
130                (None, _) => None,
131                (Some(Ok(controller)), Some(Ok(controllers))) => {
132                    let mut controllers = controllers;
133                    controllers.push(controller);
134                    Some(Ok(controllers))
135                }
136                (Some(Ok(controller)), None) => Some(Ok(vec![controller])),
137            };
138        Self {
139            controllers,
140            ..self
141        }
142    }
143
144    /// Pass in a designated controller for the canister.
145    pub fn with_controller<C, E>(self, controller: C) -> Self
146    where
147        E: std::fmt::Display,
148        C: TryInto<Principal, Error = E>,
149    {
150        self.with_optional_controller(Some(controller))
151    }
152
153    /// Pass in a compute allocation optional value for the canister. If this is [`None`],
154    /// it will revert the compute allocation to default.
155    pub fn with_optional_compute_allocation<C, E>(self, compute_allocation: Option<C>) -> Self
156    where
157        E: std::fmt::Display,
158        C: TryInto<ComputeAllocation, Error = E>,
159    {
160        Self {
161            compute_allocation: compute_allocation.map(|ca| {
162                ca.try_into()
163                    .map_err(|e| AgentError::MessageError(format!("{e}")))
164            }),
165            ..self
166        }
167    }
168
169    /// Pass in a compute allocation value for the canister.
170    pub fn with_compute_allocation<C, E>(self, compute_allocation: C) -> Self
171    where
172        E: std::fmt::Display,
173        C: TryInto<ComputeAllocation, Error = E>,
174    {
175        self.with_optional_compute_allocation(Some(compute_allocation))
176    }
177
178    /// Pass in a memory allocation optional value for the canister. If this is [`None`],
179    /// it will revert the memory allocation to default.
180    pub fn with_optional_memory_allocation<E, C>(self, memory_allocation: Option<C>) -> Self
181    where
182        E: std::fmt::Display,
183        C: TryInto<MemoryAllocation, Error = E>,
184    {
185        Self {
186            memory_allocation: memory_allocation.map(|ma| {
187                ma.try_into()
188                    .map_err(|e| AgentError::MessageError(format!("{e}")))
189            }),
190            ..self
191        }
192    }
193
194    /// Pass in a memory allocation value for the canister.
195    pub fn with_memory_allocation<C, E>(self, memory_allocation: C) -> Self
196    where
197        E: std::fmt::Display,
198        C: TryInto<MemoryAllocation, Error = E>,
199    {
200        self.with_optional_memory_allocation(Some(memory_allocation))
201    }
202
203    /// Pass in a freezing threshold optional value for the canister. If this is [`None`],
204    /// it will revert the freezing threshold to default.
205    pub fn with_optional_freezing_threshold<E, C>(self, freezing_threshold: Option<C>) -> Self
206    where
207        E: std::fmt::Display,
208        C: TryInto<FreezingThreshold, Error = E>,
209    {
210        Self {
211            freezing_threshold: freezing_threshold.map(|ma| {
212                ma.try_into()
213                    .map_err(|e| AgentError::MessageError(format!("{e}")))
214            }),
215            ..self
216        }
217    }
218
219    /// Pass in a freezing threshold value for the canister.
220    pub fn with_freezing_threshold<C, E>(self, freezing_threshold: C) -> Self
221    where
222        E: std::fmt::Display,
223        C: TryInto<FreezingThreshold, Error = E>,
224    {
225        self.with_optional_freezing_threshold(Some(freezing_threshold))
226    }
227
228    /// Pass in a reserved cycles limit value for the canister.
229    pub fn with_reserved_cycles_limit<C, E>(self, limit: C) -> Self
230    where
231        E: std::fmt::Display,
232        C: TryInto<ReservedCyclesLimit, Error = E>,
233    {
234        self.with_optional_reserved_cycles_limit(Some(limit))
235    }
236
237    /// Pass in a reserved cycles limit optional value for the canister. If this is [`None`],
238    /// it will create the canister with the default limit.
239    pub fn with_optional_reserved_cycles_limit<E, C>(self, limit: Option<C>) -> Self
240    where
241        E: std::fmt::Display,
242        C: TryInto<ReservedCyclesLimit, Error = E>,
243    {
244        Self {
245            reserved_cycles_limit: limit.map(|limit| {
246                limit
247                    .try_into()
248                    .map_err(|e| AgentError::MessageError(format!("{e}")))
249            }),
250            ..self
251        }
252    }
253
254    /// Pass in a Wasm memory limit value for the canister.
255    pub fn with_wasm_memory_limit<C, E>(self, wasm_memory_limit: C) -> Self
256    where
257        E: std::fmt::Display,
258        C: TryInto<WasmMemoryLimit, Error = E>,
259    {
260        self.with_optional_wasm_memory_limit(Some(wasm_memory_limit))
261    }
262
263    /// Pass in a Wasm memory limit optional value for the canister. If this is [`None`],
264    /// it will revert the Wasm memory limit to default.
265    pub fn with_optional_wasm_memory_limit<E, C>(self, wasm_memory_limit: Option<C>) -> Self
266    where
267        E: std::fmt::Display,
268        C: TryInto<WasmMemoryLimit, Error = E>,
269    {
270        Self {
271            wasm_memory_limit: wasm_memory_limit.map(|limit| {
272                limit
273                    .try_into()
274                    .map_err(|e| AgentError::MessageError(format!("{e}")))
275            }),
276            ..self
277        }
278    }
279
280    /// Pass in a Wasm memory threshold value for the canister.
281    pub fn with_wasm_memory_threshold<C, E>(self, wasm_memory_threshold: C) -> Self
282    where
283        E: std::fmt::Display,
284        C: TryInto<WasmMemoryLimit, Error = E>,
285    {
286        self.with_optional_wasm_memory_threshold(Some(wasm_memory_threshold))
287    }
288
289    /// Pass in a Wasm memory threshold optional value for the canister. If this is [`None`],
290    /// it will revert the Wasm memory threshold to default.
291    pub fn with_optional_wasm_memory_threshold<E, C>(self, wasm_memory_threshold: Option<C>) -> Self
292    where
293        E: std::fmt::Display,
294        C: TryInto<WasmMemoryLimit, Error = E>,
295    {
296        Self {
297            wasm_memory_threshold: wasm_memory_threshold.map(|limit| {
298                limit
299                    .try_into()
300                    .map_err(|e| AgentError::MessageError(format!("{e}")))
301            }),
302            ..self
303        }
304    }
305
306    /// Pass in a log visibility setting for the canister.
307    pub fn with_log_visibility<C, E>(self, log_visibility: C) -> Self
308    where
309        E: std::fmt::Display,
310        C: TryInto<LogVisibility, Error = E>,
311    {
312        self.with_optional_log_visibility(Some(log_visibility))
313    }
314
315    /// Pass in a log visibility optional setting for the canister. If this is [`None`],
316    /// it will revert the log visibility to default.
317    pub fn with_optional_log_visibility<E, C>(self, log_visibility: Option<C>) -> Self
318    where
319        E: std::fmt::Display,
320        C: TryInto<LogVisibility, Error = E>,
321    {
322        Self {
323            log_visibility: log_visibility.map(|visibility| {
324                visibility
325                    .try_into()
326                    .map_err(|e| AgentError::MessageError(format!("{e}")))
327            }),
328            ..self
329        }
330    }
331
332    /// Pass in a environment variables setting for the canister.
333    pub fn with_environment_variables<E, C>(self, environment_variables: C) -> Self
334    where
335        E: std::fmt::Display,
336        C: TryInto<Vec<EnvironmentVariable>, Error = E>,
337    {
338        self.with_optional_environment_variables(Some(environment_variables))
339    }
340
341    /// Pass in a environment variables optional setting for the canister. If this is [`None`],
342    /// it will revert the environment variables to default.
343    pub fn with_optional_environment_variables<E, C>(self, environment_variables: Option<C>) -> Self
344    where
345        E: std::fmt::Display,
346        C: TryInto<Vec<EnvironmentVariable>, Error = E>,
347    {
348        Self {
349            environment_variables: environment_variables.map(|vars| {
350                vars.try_into()
351                    .map_err(|e| AgentError::MessageError(format!("{e}")))
352            }),
353            ..self
354        }
355    }
356
357    /// Create an [`AsyncCall`] implementation that, when called, will create a
358    /// canister.
359    pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = (Principal,)>, AgentError> {
360        let controllers = match self.controllers {
361            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
362            Some(Ok(x)) => Some(x),
363            None => None,
364        };
365        let compute_allocation = match self.compute_allocation {
366            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
367            Some(Ok(x)) => Some(Nat::from(u8::from(x))),
368            None => None,
369        };
370        let memory_allocation = match self.memory_allocation {
371            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
372            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
373            None => None,
374        };
375        let freezing_threshold = match self.freezing_threshold {
376            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
377            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
378            None => None,
379        };
380        let reserved_cycles_limit = match self.reserved_cycles_limit {
381            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
382            Some(Ok(x)) => Some(Nat::from(u128::from(x))),
383            None => None,
384        };
385        let wasm_memory_limit = match self.wasm_memory_limit {
386            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
387            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
388            None => None,
389        };
390        let wasm_memory_threshold = match self.wasm_memory_threshold {
391            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
392            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
393            None => None,
394        };
395        let log_visibility = match self.log_visibility {
396            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
397            Some(Ok(x)) => Some(x),
398            None => None,
399        };
400        let environment_variables = match self.environment_variables {
401            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
402            Some(Ok(x)) => Some(x),
403            None => None,
404        };
405
406        #[derive(Deserialize, CandidType)]
407        struct Out {
408            canister_id: Principal,
409        }
410
411        let async_builder = if self.is_provisional_create {
412            #[derive(CandidType)]
413            struct In {
414                amount: Option<Nat>,
415                settings: CanisterSettings,
416                specified_id: Option<Principal>,
417            }
418            let in_arg = In {
419                amount: self.amount.map(Nat::from),
420                settings: CanisterSettings {
421                    controllers,
422                    compute_allocation,
423                    memory_allocation,
424                    freezing_threshold,
425                    reserved_cycles_limit,
426                    wasm_memory_limit,
427                    wasm_memory_threshold,
428                    log_visibility,
429                    environment_variables,
430                },
431                specified_id: self.specified_id,
432            };
433            self.canister
434                .update(MgmtMethod::ProvisionalCreateCanisterWithCycles.as_ref())
435                .with_arg(in_arg)
436                .with_effective_canister_id(self.effective_canister_id)
437        } else {
438            self.canister
439                .update(MgmtMethod::CreateCanister.as_ref())
440                .with_arg(CanisterSettings {
441                    controllers,
442                    compute_allocation,
443                    memory_allocation,
444                    freezing_threshold,
445                    reserved_cycles_limit,
446                    wasm_memory_limit,
447                    wasm_memory_threshold,
448                    log_visibility,
449                    environment_variables,
450                })
451                .with_effective_canister_id(self.effective_canister_id)
452        };
453
454        Ok(async_builder
455            .build()
456            .map(|result: (Out,)| (result.0.canister_id,)))
457    }
458
459    /// Make a call. This is equivalent to the [`AsyncCall::call`].
460    pub async fn call(self) -> Result<CallResponse<(Principal,)>, AgentError> {
461        self.build()?.call().await
462    }
463
464    /// Make a call. This is equivalent to the [`AsyncCall::call_and_wait`].
465    pub async fn call_and_wait(self) -> Result<(Principal,), AgentError> {
466        self.build()?.call_and_wait().await
467    }
468}
469
470#[cfg_attr(target_family = "wasm", async_trait(?Send))]
471#[cfg_attr(not(target_family = "wasm"), async_trait)]
472impl<'agent, 'canister: 'agent> AsyncCall for CreateCanisterBuilder<'agent, 'canister> {
473    type Value = (Principal,);
474
475    async fn call(self) -> Result<CallResponse<(Principal,)>, AgentError> {
476        self.build()?.call().await
477    }
478
479    async fn call_and_wait(self) -> Result<(Principal,), AgentError> {
480        self.build()?.call_and_wait().await
481    }
482}
483
484impl<'agent, 'canister: 'agent> IntoFuture for CreateCanisterBuilder<'agent, 'canister> {
485    type IntoFuture = CallFuture<'agent, (Principal,)>;
486    type Output = Result<(Principal,), AgentError>;
487
488    fn into_future(self) -> Self::IntoFuture {
489        AsyncCall::call_and_wait(self)
490    }
491}
492
493#[doc(hidden)]
494#[deprecated(since = "0.42.0", note = "Please use UpgradeFlags instead")]
495pub type CanisterUpgradeOptions = UpgradeFlags;
496
497#[doc(hidden)]
498#[deprecated(since = "0.42.0", note = "Please use CanisterInstallMode instead")]
499pub type InstallMode = CanisterInstallMode;
500
501#[doc(hidden)]
502#[deprecated(since = "0.42.0", note = "Please use InstallCodeArgs instead")]
503pub type CanisterInstall = InstallCodeArgs;
504
505/// A builder for an `install_code` call.
506#[derive(Debug)]
507pub struct InstallCodeBuilder<'agent, 'canister: 'agent> {
508    canister: &'canister Canister<'agent>,
509    canister_id: Principal,
510    wasm: &'canister [u8],
511    arg: Argument,
512    mode: Option<CanisterInstallMode>,
513}
514
515impl<'agent, 'canister: 'agent> InstallCodeBuilder<'agent, 'canister> {
516    /// Create an `InstallCode` builder, which is also an `AsyncCall` implementation.
517    pub fn builder(
518        canister: &'canister Canister<'agent>,
519        canister_id: &Principal,
520        wasm: &'canister [u8],
521    ) -> Self {
522        Self {
523            canister,
524            canister_id: *canister_id,
525            wasm,
526            arg: Default::default(),
527            mode: None,
528        }
529    }
530
531    /// Set the argument to the installation, which will be passed to the init
532    /// method of the canister. Can be called at most once.
533    pub fn with_arg<Argument: CandidType>(
534        mut self,
535        arg: Argument,
536    ) -> InstallCodeBuilder<'agent, 'canister> {
537        self.arg.set_idl_arg(arg);
538        self
539    }
540    /// Set the argument with multiple arguments as tuple to the installation,
541    /// which will be passed to the init method of the canister. Can be called at most once.
542    pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
543        assert!(self.arg.0.is_none(), "argument is being set more than once");
544        self.arg = Argument::from_candid(tuple);
545        self
546    }
547    /// Set the argument passed in to the canister with raw bytes. Can be called at most once.
548    pub fn with_raw_arg(mut self, arg: Vec<u8>) -> InstallCodeBuilder<'agent, 'canister> {
549        self.arg.set_raw_arg(arg);
550        self
551    }
552
553    /// Pass in the [`CanisterInstallMode`].
554    pub fn with_mode(self, mode: CanisterInstallMode) -> Self {
555        Self {
556            mode: Some(mode),
557            ..self
558        }
559    }
560
561    /// Create an [`AsyncCall`] implementation that, when called, will install the
562    /// canister.
563    pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
564        Ok(self
565            .canister
566            .update(MgmtMethod::InstallCode.as_ref())
567            .with_arg(InstallCodeArgs {
568                mode: self.mode.unwrap_or(CanisterInstallMode::Install),
569                canister_id: self.canister_id,
570                wasm_module: self.wasm.to_owned(),
571                arg: self.arg.serialize()?,
572                sender_canister_version: None,
573            })
574            .with_effective_canister_id(self.canister_id)
575            .build())
576    }
577
578    /// Make a call. This is equivalent to the [`AsyncCall::call`].
579    pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
580        self.build()?.call().await
581    }
582
583    /// Make a call. This is equivalent to the [`AsyncCall::call_and_wait`].
584    pub async fn call_and_wait(self) -> Result<(), AgentError> {
585        self.build()?.call_and_wait().await
586    }
587}
588
589#[cfg_attr(target_family = "wasm", async_trait(?Send))]
590#[cfg_attr(not(target_family = "wasm"), async_trait)]
591impl<'agent, 'canister: 'agent> AsyncCall for InstallCodeBuilder<'agent, 'canister> {
592    type Value = ();
593
594    async fn call(self) -> Result<CallResponse<()>, AgentError> {
595        self.build()?.call().await
596    }
597
598    async fn call_and_wait(self) -> Result<(), AgentError> {
599        self.build()?.call_and_wait().await
600    }
601}
602
603impl<'agent, 'canister: 'agent> IntoFuture for InstallCodeBuilder<'agent, 'canister> {
604    type IntoFuture = CallFuture<'agent, ()>;
605    type Output = Result<(), AgentError>;
606
607    fn into_future(self) -> Self::IntoFuture {
608        AsyncCall::call_and_wait(self)
609    }
610}
611
612/// A builder for an `install_chunked_code` call.
613#[derive(Debug)]
614pub struct InstallChunkedCodeBuilder<'agent, 'canister> {
615    canister: &'canister Canister<'agent>,
616    target_canister: Principal,
617    store_canister: Option<Principal>,
618    chunk_hashes_list: Vec<ChunkHash>,
619    wasm_module_hash: Vec<u8>,
620    arg: Argument,
621    mode: CanisterInstallMode,
622}
623
624impl<'agent: 'canister, 'canister> InstallChunkedCodeBuilder<'agent, 'canister> {
625    /// Create an `InstallChunkedCodeBuilder`.
626    pub fn builder(
627        canister: &'canister Canister<'agent>,
628        target_canister: Principal,
629        wasm_module_hash: &[u8],
630    ) -> Self {
631        Self {
632            canister,
633            target_canister,
634            wasm_module_hash: wasm_module_hash.to_vec(),
635            store_canister: None,
636            chunk_hashes_list: vec![],
637            arg: Argument::new(),
638            mode: CanisterInstallMode::Install,
639        }
640    }
641
642    /// Set the chunks to install. These must previously have been set with [`ManagementCanister::upload_chunk`].
643    pub fn with_chunk_hashes(mut self, chunk_hashes: Vec<ChunkHash>) -> Self {
644        self.chunk_hashes_list = chunk_hashes;
645        self
646    }
647
648    /// Set the canister to pull uploaded chunks from. By default this is the same as the target canister.
649    pub fn with_store_canister(mut self, store_canister: Principal) -> Self {
650        self.store_canister = Some(store_canister);
651        self
652    }
653
654    /// Set the argument to the installation, which will be passed to the init
655    /// method of the canister. Can be called at most once.
656    pub fn with_arg(mut self, argument: impl CandidType) -> Self {
657        self.arg.set_idl_arg(argument);
658        self
659    }
660
661    /// Set the argument with multiple arguments as tuple to the installation,
662    /// which will be passed to the init method of the canister. Can be called at most once.
663    pub fn with_args(mut self, argument: impl ArgumentEncoder) -> Self {
664        assert!(self.arg.0.is_none(), "argument is being set more than once");
665        self.arg = Argument::from_candid(argument);
666        self
667    }
668
669    /// Set the argument passed in to the canister with raw bytes. Can be called at most once.
670    pub fn with_raw_arg(mut self, argument: Vec<u8>) -> Self {
671        self.arg.set_raw_arg(argument);
672        self
673    }
674
675    /// Set the [`CanisterInstallMode`].
676    pub fn with_install_mode(mut self, mode: CanisterInstallMode) -> Self {
677        self.mode = mode;
678        self
679    }
680
681    /// Create an [`AsyncCall`] implementation that, when called, will install the canister.
682    pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
683        #[derive(CandidType)]
684        struct In {
685            mode: CanisterInstallMode,
686            target_canister: Principal,
687            store_canister: Option<Principal>,
688            chunk_hashes_list: Vec<ChunkHash>,
689            wasm_module_hash: Vec<u8>,
690            arg: Vec<u8>,
691            sender_canister_version: Option<u64>,
692        }
693        let Self {
694            mode,
695            target_canister,
696            store_canister,
697            chunk_hashes_list,
698            wasm_module_hash,
699            arg,
700            ..
701        } = self;
702        Ok(self
703            .canister
704            .update(MgmtMethod::InstallChunkedCode.as_ref())
705            .with_arg(In {
706                mode,
707                target_canister,
708                store_canister,
709                chunk_hashes_list,
710                wasm_module_hash,
711                arg: arg.serialize()?,
712                sender_canister_version: None,
713            })
714            .with_effective_canister_id(target_canister)
715            .build())
716    }
717
718    /// Make the call. This is equivalent to [`AsyncCall::call`].
719    pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
720        self.build()?.call().await
721    }
722
723    /// Make the call. This is equivalent to [`AsyncCall::call_and_wait`].
724    pub async fn call_and_wait(self) -> Result<(), AgentError> {
725        self.build()?.call_and_wait().await
726    }
727}
728
729#[cfg_attr(target_family = "wasm", async_trait(?Send))]
730#[cfg_attr(not(target_family = "wasm"), async_trait)]
731impl<'agent, 'canister: 'agent> AsyncCall for InstallChunkedCodeBuilder<'agent, 'canister> {
732    type Value = ();
733
734    async fn call(self) -> Result<CallResponse<()>, AgentError> {
735        self.call().await
736    }
737
738    async fn call_and_wait(self) -> Result<(), AgentError> {
739        self.call_and_wait().await
740    }
741}
742
743impl<'agent, 'canister: 'agent> IntoFuture for InstallChunkedCodeBuilder<'agent, 'canister> {
744    type IntoFuture = CallFuture<'agent, ()>;
745    type Output = Result<(), AgentError>;
746
747    fn into_future(self) -> Self::IntoFuture {
748        AsyncCall::call_and_wait(self)
749    }
750}
751
752/// A builder for a [`ManagementCanister::install`] call. This automatically selects one-shot installation or chunked installation depending on module size.
753///
754/// # Warnings
755///
756/// This will clear chunked code storage if chunked installation is used. Do not use with canisters that you are manually uploading chunked code to.
757#[derive(Debug)]
758pub struct InstallBuilder<'agent, 'canister, 'builder> {
759    canister: &'canister ManagementCanister<'agent>,
760    canister_id: Principal,
761    // more precise lifetimes are used here at risk of annoying the user
762    // because `wasm` may be memory-mapped which is tricky to lifetime
763    wasm: &'builder [u8],
764    arg: Argument,
765    mode: CanisterInstallMode,
766}
767
768impl<'agent: 'canister, 'canister: 'builder, 'builder> InstallBuilder<'agent, 'canister, 'builder> {
769    // Messages are a maximum of 2MiB. Thus basic installation should cap the wasm and arg size at 1.85MiB, since
770    // the current API is definitely not going to produce 150KiB of framing data for it.
771    const CHUNK_CUTOFF: usize = (1.85 * 1024. * 1024.) as usize;
772
773    /// Create a canister installation builder.
774    pub fn builder(
775        canister: &'canister ManagementCanister<'agent>,
776        canister_id: &Principal,
777        wasm: &'builder [u8],
778    ) -> Self {
779        Self {
780            canister,
781            canister_id: *canister_id,
782            wasm,
783            arg: Default::default(),
784            mode: CanisterInstallMode::Install,
785        }
786    }
787
788    /// Set the argument to the installation, which will be passed to the init
789    /// method of the canister. Can be called at most once.
790    pub fn with_arg<Argument: CandidType>(mut self, arg: Argument) -> Self {
791        self.arg.set_idl_arg(arg);
792        self
793    }
794    /// Set the argument with multiple arguments as tuple to the installation,
795    /// which will be passed to the init method of the canister. Can be called at most once.
796    pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
797        assert!(self.arg.0.is_none(), "argument is being set more than once");
798        self.arg = Argument::from_candid(tuple);
799        self
800    }
801    /// Set the argument passed in to the canister with raw bytes. Can be called at most once.
802    pub fn with_raw_arg(mut self, arg: Vec<u8>) -> Self {
803        self.arg.set_raw_arg(arg);
804        self
805    }
806
807    /// Pass in the [`CanisterInstallMode`].
808    pub fn with_mode(self, mode: CanisterInstallMode) -> Self {
809        Self { mode, ..self }
810    }
811
812    /// Invoke the installation process. This may result in many calls which may take several seconds;
813    /// use [`call_and_wait_with_progress`](Self::call_and_wait_with_progress) if you want progress reporting.
814    pub async fn call_and_wait(self) -> Result<(), AgentError> {
815        self.call_and_wait_with_progress()
816            .await
817            .try_for_each(|_| ready(Ok(())))
818            .await
819    }
820
821    /// Invoke the installation process. The returned stream must be iterated to completion; it is used to track progress,
822    /// as installation may take arbitrarily long, and is intended to be passed to functions like `indicatif::ProgressBar::wrap_stream`.
823    /// There are exactly [`size_hint().0`](Stream::size_hint) steps.
824    pub async fn call_and_wait_with_progress(
825        self,
826    ) -> impl Stream<Item = Result<(), AgentError>> + 'builder {
827        let stream_res = /* try { */ async move {
828            let arg = self.arg.serialize()?;
829            let stream: BoxStream<'_, _> =
830                if self.wasm.len() + arg.len() < Self::CHUNK_CUTOFF {
831                    Box::pin(
832                        async move {
833                            self.canister
834                                .install_code(&self.canister_id, self.wasm)
835                                .with_raw_arg(arg)
836                                .with_mode(self.mode)
837                                .call_and_wait()
838                                .await
839                        }
840                        .into_stream(),
841                    )
842                } else {
843                    let (existing_chunks,) = self.canister.stored_chunks(&self.canister_id).call_and_wait().await?;
844                    let existing_chunks = existing_chunks.into_iter().map(|c| c.hash).collect::<BTreeSet<_>>();
845                    let all_chunks = self.wasm.chunks(1024 * 1024).map(|x| (Sha256::digest(x).to_vec(), x)).collect::<Vec<_>>();
846                    let mut to_upload_chunks = vec![];
847                    for (hash, chunk) in &all_chunks {
848                        if !existing_chunks.contains(hash) {
849                            to_upload_chunks.push(*chunk);
850                        }
851                    }
852
853                    let upload_chunks_stream = FuturesUnordered::new();
854                    for chunk in to_upload_chunks {
855                        upload_chunks_stream.push(async move {
856                            let (_res,) = self
857                                .canister
858                                .upload_chunk(&self.canister_id, &ic_management_canister_types::UploadChunkArgs {
859                                    canister_id: self.canister_id,
860                                    chunk: chunk.to_vec(),
861                                })
862                                .call_and_wait()
863                                .await?;
864                            Ok(())
865                        });
866                    }
867                    let install_chunked_code_stream = async move {
868                        let results = all_chunks.iter().map(|(hash,_)| ChunkHash{ hash: hash.clone() }).collect();
869                        self.canister
870                            .install_chunked_code(
871                                &self.canister_id,
872                                &Sha256::digest(self.wasm),
873                            )
874                            .with_chunk_hashes(results)
875                            .with_raw_arg(arg)
876                            .with_install_mode(self.mode)
877                            .call_and_wait()
878                            .await
879                    }
880                    .into_stream();
881                    let clear_store_stream = async move {
882                        self.canister
883                            .clear_chunk_store(&self.canister_id)
884                            .call_and_wait()
885                            .await
886                    }
887                    .into_stream();
888
889                    Box::pin(
890                        upload_chunks_stream
891                            .chain(install_chunked_code_stream)
892                            .chain(clear_store_stream                        ),
893                    )
894                };
895            Ok(stream)
896        }.await;
897        match stream_res {
898            Ok(stream) => stream,
899            Err(err) => Box::pin(stream::once(async { Err(err) })),
900        }
901    }
902}
903
904impl<'agent: 'canister, 'canister: 'builder, 'builder> IntoFuture
905    for InstallBuilder<'agent, 'canister, 'builder>
906{
907    type IntoFuture = CallFuture<'builder, ()>;
908    type Output = Result<(), AgentError>;
909
910    fn into_future(self) -> Self::IntoFuture {
911        Box::pin(self.call_and_wait())
912    }
913}
914
915/// A builder for an `update_settings` call.
916#[derive(Debug)]
917pub struct UpdateCanisterBuilder<'agent, 'canister: 'agent> {
918    canister: &'canister Canister<'agent>,
919    canister_id: Principal,
920    controllers: Option<Result<Vec<Principal>, AgentError>>,
921    compute_allocation: Option<Result<ComputeAllocation, AgentError>>,
922    memory_allocation: Option<Result<MemoryAllocation, AgentError>>,
923    freezing_threshold: Option<Result<FreezingThreshold, AgentError>>,
924    reserved_cycles_limit: Option<Result<ReservedCyclesLimit, AgentError>>,
925    wasm_memory_limit: Option<Result<WasmMemoryLimit, AgentError>>,
926    wasm_memory_threshold: Option<Result<WasmMemoryLimit, AgentError>>,
927    log_visibility: Option<Result<LogVisibility, AgentError>>,
928    environment_variables: Option<Result<Vec<EnvironmentVariable>, AgentError>>,
929}
930
931impl<'agent, 'canister: 'agent> UpdateCanisterBuilder<'agent, 'canister> {
932    /// Create an `UpdateCanister` builder, which is also an `AsyncCall` implementation.
933    pub fn builder(canister: &'canister Canister<'agent>, canister_id: &Principal) -> Self {
934        Self {
935            canister,
936            canister_id: *canister_id,
937            controllers: None,
938            compute_allocation: None,
939            memory_allocation: None,
940            freezing_threshold: None,
941            reserved_cycles_limit: None,
942            wasm_memory_limit: None,
943            wasm_memory_threshold: None,
944            log_visibility: None,
945            environment_variables: None,
946        }
947    }
948
949    /// Pass in an optional controller for the canister. If this is [`None`],
950    /// it will revert the controller to default.
951    pub fn with_optional_controller<C, E>(self, controller: Option<C>) -> Self
952    where
953        E: std::fmt::Display,
954        C: TryInto<Principal, Error = E>,
955    {
956        let controller_to_add: Option<Result<Principal, _>> = controller.map(|ca| {
957            ca.try_into()
958                .map_err(|e| AgentError::MessageError(format!("{e}")))
959        });
960        let controllers: Option<Result<Vec<Principal>, _>> =
961            match (controller_to_add, self.controllers) {
962                (_, Some(Err(sticky))) => Some(Err(sticky)),
963                (Some(Err(e)), _) => Some(Err(e)),
964                (None, _) => None,
965                (Some(Ok(controller)), Some(Ok(controllers))) => {
966                    let mut controllers = controllers;
967                    controllers.push(controller);
968                    Some(Ok(controllers))
969                }
970                (Some(Ok(controller)), None) => Some(Ok(vec![controller])),
971            };
972
973        Self {
974            controllers,
975            ..self
976        }
977    }
978
979    /// Pass in a designated controller for the canister.
980    pub fn with_controller<C, E>(self, controller: C) -> Self
981    where
982        E: std::fmt::Display,
983        C: TryInto<Principal, Error = E>,
984    {
985        self.with_optional_controller(Some(controller))
986    }
987
988    /// Pass in a compute allocation optional value for the canister. If this is [`None`],
989    /// it will revert the compute allocation to default.
990    pub fn with_optional_compute_allocation<C, E>(self, compute_allocation: Option<C>) -> Self
991    where
992        E: std::fmt::Display,
993        C: TryInto<ComputeAllocation, Error = E>,
994    {
995        Self {
996            compute_allocation: compute_allocation.map(|ca| {
997                ca.try_into()
998                    .map_err(|e| AgentError::MessageError(format!("{e}")))
999            }),
1000            ..self
1001        }
1002    }
1003
1004    /// Pass in a compute allocation value for the canister.
1005    pub fn with_compute_allocation<C, E>(self, compute_allocation: C) -> Self
1006    where
1007        E: std::fmt::Display,
1008        C: TryInto<ComputeAllocation, Error = E>,
1009    {
1010        self.with_optional_compute_allocation(Some(compute_allocation))
1011    }
1012
1013    /// Pass in a memory allocation optional value for the canister. If this is [`None`],
1014    /// it will revert the memory allocation to default.
1015    pub fn with_optional_memory_allocation<E, C>(self, memory_allocation: Option<C>) -> Self
1016    where
1017        E: std::fmt::Display,
1018        C: TryInto<MemoryAllocation, Error = E>,
1019    {
1020        Self {
1021            memory_allocation: memory_allocation.map(|ma| {
1022                ma.try_into()
1023                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1024            }),
1025            ..self
1026        }
1027    }
1028
1029    /// Pass in a memory allocation value for the canister.
1030    pub fn with_memory_allocation<C, E>(self, memory_allocation: C) -> Self
1031    where
1032        E: std::fmt::Display,
1033        C: TryInto<MemoryAllocation, Error = E>,
1034    {
1035        self.with_optional_memory_allocation(Some(memory_allocation))
1036    }
1037
1038    /// Pass in a freezing threshold optional value for the canister. If this is [`None`],
1039    /// it will revert the freezing threshold to default.
1040    pub fn with_optional_freezing_threshold<E, C>(self, freezing_threshold: Option<C>) -> Self
1041    where
1042        E: std::fmt::Display,
1043        C: TryInto<FreezingThreshold, Error = E>,
1044    {
1045        Self {
1046            freezing_threshold: freezing_threshold.map(|ma| {
1047                ma.try_into()
1048                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1049            }),
1050            ..self
1051        }
1052    }
1053
1054    /// Pass in a freezing threshold value for the canister.
1055    pub fn with_freezing_threshold<C, E>(self, freezing_threshold: C) -> Self
1056    where
1057        E: std::fmt::Display,
1058        C: TryInto<FreezingThreshold, Error = E>,
1059    {
1060        self.with_optional_freezing_threshold(Some(freezing_threshold))
1061    }
1062
1063    /// Pass in a reserved cycles limit value for the canister.
1064    pub fn with_reserved_cycles_limit<C, E>(self, limit: C) -> Self
1065    where
1066        E: std::fmt::Display,
1067        C: TryInto<ReservedCyclesLimit, Error = E>,
1068    {
1069        self.with_optional_reserved_cycles_limit(Some(limit))
1070    }
1071
1072    /// Pass in a reserved cycles limit optional value for the canister.
1073    /// If this is [`None`], leaves the reserved cycles limit unchanged.
1074    pub fn with_optional_reserved_cycles_limit<E, C>(self, limit: Option<C>) -> Self
1075    where
1076        E: std::fmt::Display,
1077        C: TryInto<ReservedCyclesLimit, Error = E>,
1078    {
1079        Self {
1080            reserved_cycles_limit: limit.map(|ma| {
1081                ma.try_into()
1082                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1083            }),
1084            ..self
1085        }
1086    }
1087
1088    /// Pass in a Wasm memory limit value for the canister.
1089    pub fn with_wasm_memory_limit<C, E>(self, wasm_memory_limit: C) -> Self
1090    where
1091        E: std::fmt::Display,
1092        C: TryInto<WasmMemoryLimit, Error = E>,
1093    {
1094        self.with_optional_wasm_memory_limit(Some(wasm_memory_limit))
1095    }
1096
1097    /// Pass in a Wasm memory limit optional value for the canister. If this is [`None`],
1098    /// leaves the Wasm memory limit unchanged.
1099    pub fn with_optional_wasm_memory_limit<E, C>(self, wasm_memory_limit: Option<C>) -> Self
1100    where
1101        E: std::fmt::Display,
1102        C: TryInto<WasmMemoryLimit, Error = E>,
1103    {
1104        Self {
1105            wasm_memory_limit: wasm_memory_limit.map(|limit| {
1106                limit
1107                    .try_into()
1108                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1109            }),
1110            ..self
1111        }
1112    }
1113
1114    /// Pass in a Wasm memory threshold value for the canister.
1115    pub fn with_wasm_memory_threshold<C, E>(self, wasm_memory_threshold: C) -> Self
1116    where
1117        E: std::fmt::Display,
1118        C: TryInto<WasmMemoryLimit, Error = E>,
1119    {
1120        self.with_optional_wasm_memory_threshold(Some(wasm_memory_threshold))
1121    }
1122
1123    /// Pass in a Wasm memory threshold value for the canister. If this is [`None`],
1124    /// leaves the memory threshold unchanged.
1125    pub fn with_optional_wasm_memory_threshold<E, C>(self, wasm_memory_threshold: Option<C>) -> Self
1126    where
1127        E: std::fmt::Display,
1128        C: TryInto<WasmMemoryLimit, Error = E>,
1129    {
1130        Self {
1131            wasm_memory_threshold: wasm_memory_threshold.map(|limit| {
1132                limit
1133                    .try_into()
1134                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1135            }),
1136            ..self
1137        }
1138    }
1139
1140    /// Pass in a log visibility setting for the canister.
1141    pub fn with_log_visibility<C, E>(self, log_visibility: C) -> Self
1142    where
1143        E: std::fmt::Display,
1144        C: TryInto<LogVisibility, Error = E>,
1145    {
1146        self.with_optional_log_visibility(Some(log_visibility))
1147    }
1148
1149    /// Pass in a log visibility optional setting for the canister. If this is [`None`],
1150    /// leaves the log visibility unchanged.
1151    pub fn with_optional_log_visibility<E, C>(self, log_visibility: Option<C>) -> Self
1152    where
1153        E: std::fmt::Display,
1154        C: TryInto<LogVisibility, Error = E>,
1155    {
1156        Self {
1157            log_visibility: log_visibility.map(|limit| {
1158                limit
1159                    .try_into()
1160                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1161            }),
1162            ..self
1163        }
1164    }
1165
1166    /// Pass in a environment variables setting for the canister.
1167    pub fn with_environment_variables<C, E>(self, environment_variables: C) -> Self
1168    where
1169        E: std::fmt::Display,
1170        C: TryInto<Vec<EnvironmentVariable>, Error = E>,
1171    {
1172        self.with_optional_environment_variables(Some(environment_variables))
1173    }
1174
1175    /// Pass in a environment variables optional setting for the canister. If this is [`None`],
1176    /// leaves the environment variables unchanged.
1177    pub fn with_optional_environment_variables<E, C>(self, environment_variables: Option<C>) -> Self
1178    where
1179        E: std::fmt::Display,
1180        C: TryInto<Vec<EnvironmentVariable>, Error = E>,
1181    {
1182        Self {
1183            environment_variables: environment_variables.map(|vars| {
1184                vars.try_into()
1185                    .map_err(|e| AgentError::MessageError(format!("{e}")))
1186            }),
1187            ..self
1188        }
1189    }
1190
1191    /// Create an [`AsyncCall`] implementation that, when called, will update a
1192    /// canisters settings.
1193    pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
1194        #[derive(CandidType)]
1195        struct In {
1196            canister_id: Principal,
1197            settings: CanisterSettings,
1198        }
1199
1200        let controllers = match self.controllers {
1201            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1202            Some(Ok(x)) => Some(x),
1203            None => None,
1204        };
1205        let compute_allocation = match self.compute_allocation {
1206            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1207            Some(Ok(x)) => Some(Nat::from(u8::from(x))),
1208            None => None,
1209        };
1210        let memory_allocation = match self.memory_allocation {
1211            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1212            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1213            None => None,
1214        };
1215        let freezing_threshold = match self.freezing_threshold {
1216            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1217            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1218            None => None,
1219        };
1220        let reserved_cycles_limit = match self.reserved_cycles_limit {
1221            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1222            Some(Ok(x)) => Some(Nat::from(u128::from(x))),
1223            None => None,
1224        };
1225        let wasm_memory_limit = match self.wasm_memory_limit {
1226            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1227            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1228            None => None,
1229        };
1230        let wasm_memory_threshold = match self.wasm_memory_threshold {
1231            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1232            Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1233            None => None,
1234        };
1235        let log_visibility = match self.log_visibility {
1236            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1237            Some(Ok(x)) => Some(x),
1238            None => None,
1239        };
1240        let environment_variables = match self.environment_variables {
1241            Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1242            Some(Ok(x)) => Some(x),
1243            None => None,
1244        };
1245
1246        Ok(self
1247            .canister
1248            .update(MgmtMethod::UpdateSettings.as_ref())
1249            .with_arg(In {
1250                canister_id: self.canister_id,
1251                settings: CanisterSettings {
1252                    controllers,
1253                    compute_allocation,
1254                    memory_allocation,
1255                    freezing_threshold,
1256                    reserved_cycles_limit,
1257                    wasm_memory_limit,
1258                    wasm_memory_threshold,
1259                    log_visibility,
1260                    environment_variables,
1261                },
1262            })
1263            .with_effective_canister_id(self.canister_id)
1264            .build())
1265    }
1266
1267    /// Make a call. This is equivalent to the [`AsyncCall::call`].
1268    pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
1269        self.build()?.call().await
1270    }
1271
1272    /// Make a call. This is equivalent to the [`AsyncCall::call_and_wait`].
1273    pub async fn call_and_wait(self) -> Result<(), AgentError> {
1274        self.build()?.call_and_wait().await
1275    }
1276}
1277
1278#[cfg_attr(target_family = "wasm", async_trait(?Send))]
1279#[cfg_attr(not(target_family = "wasm"), async_trait)]
1280impl<'agent, 'canister: 'agent> AsyncCall for UpdateCanisterBuilder<'agent, 'canister> {
1281    type Value = ();
1282    async fn call(self) -> Result<CallResponse<()>, AgentError> {
1283        self.build()?.call().await
1284    }
1285
1286    async fn call_and_wait(self) -> Result<(), AgentError> {
1287        self.build()?.call_and_wait().await
1288    }
1289}
1290
1291impl<'agent, 'canister: 'agent> IntoFuture for UpdateCanisterBuilder<'agent, 'canister> {
1292    type IntoFuture = CallFuture<'agent, ()>;
1293    type Output = Result<(), AgentError>;
1294    fn into_future(self) -> Self::IntoFuture {
1295        AsyncCall::call_and_wait(self)
1296    }
1297}
1298
1299#[cfg(not(target_family = "wasm"))]
1300type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + Send + 'a>>;
1301#[cfg(target_family = "wasm")]
1302type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + 'a>>;