ic_cdk/api/
call.rs

1//! APIs to make and manage calls in the canister.
2use crate::api::trap;
3use candid::utils::{ArgumentDecoder, ArgumentEncoder};
4use candid::{decode_args, encode_args, write_args, CandidType, Deserialize, Principal};
5use serde::ser::Error;
6use std::future::Future;
7use std::marker::PhantomData;
8use std::pin::Pin;
9use std::sync::atomic::Ordering;
10use std::sync::{Arc, RwLock, Weak};
11use std::task::{Context, Poll, Waker};
12
13/// Rejection code from calling another canister.
14///
15/// These can be obtained either using `reject_code()` or `reject_result()`.
16#[allow(missing_docs)]
17#[repr(i32)]
18#[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
19pub enum RejectionCode {
20    NoError = 0,
21
22    SysFatal = 1,
23    SysTransient = 2,
24    DestinationInvalid = 3,
25    CanisterReject = 4,
26    CanisterError = 5,
27
28    Unknown,
29}
30
31impl From<i32> for RejectionCode {
32    fn from(code: i32) -> Self {
33        match code {
34            0 => RejectionCode::NoError,
35            1 => RejectionCode::SysFatal,
36            2 => RejectionCode::SysTransient,
37            3 => RejectionCode::DestinationInvalid,
38            4 => RejectionCode::CanisterReject,
39            5 => RejectionCode::CanisterError,
40            _ => RejectionCode::Unknown,
41        }
42    }
43}
44
45impl From<u32> for RejectionCode {
46    fn from(code: u32) -> Self {
47        RejectionCode::from(code as i32)
48    }
49}
50
51/// The result of a Call.
52///
53/// Errors on the IC have two components; a Code and a message associated with it.
54pub type CallResult<R> = Result<R, (RejectionCode, String)>;
55
56// Internal state for the Future when sending a call.
57struct CallFutureState<T: AsRef<[u8]>> {
58    result: Option<CallResult<Vec<u8>>>,
59    waker: Option<Waker>,
60    id: Principal,
61    method: String,
62    arg: T,
63    payment: u128,
64}
65
66struct CallFuture<T: AsRef<[u8]>> {
67    state: Arc<RwLock<CallFutureState<T>>>,
68}
69
70impl<T: AsRef<[u8]>> Future for CallFuture<T> {
71    type Output = CallResult<Vec<u8>>;
72
73    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
74        let self_ref = Pin::into_inner(self);
75        let mut state = self_ref.state.write().unwrap();
76
77        if let Some(result) = state.result.take() {
78            Poll::Ready(result)
79        } else {
80            if state.waker.is_none() {
81                let callee = state.id.as_slice();
82                let method = &state.method;
83                let args = state.arg.as_ref();
84                let payment = state.payment;
85                let state_ptr = Weak::into_raw(Arc::downgrade(&self_ref.state));
86                // SAFETY:
87                // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
88                // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
89                // `callback` is a function with signature (env : i32) -> () and therefore can be called as both reply and reject fn for ic0.call_new.
90                // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `callback`.
91                // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
92                // `cleanup` is a function with signature (env : i32) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup.
93                // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `cleanup`.
94                // ic0.call_perform is always safe to call.
95                // callback and cleanup are safe to parameterize with T because:
96                // - if the future is dropped before the callback is called, there will be no more strong references and the weak reference will fail to upgrade
97                // - if the future is *not* dropped before the callback is called, the compiler will mandate that any data borrowed by T is still alive
98                let err_code = unsafe {
99                    ic0::call_new(
100                        callee.as_ptr() as i32,
101                        callee.len() as i32,
102                        method.as_ptr() as i32,
103                        method.len() as i32,
104                        callback::<T> as usize as i32,
105                        state_ptr as i32,
106                        callback::<T> as usize as i32,
107                        state_ptr as i32,
108                    );
109
110                    ic0::call_data_append(args.as_ptr() as i32, args.len() as i32);
111                    add_payment(payment);
112                    ic0::call_on_cleanup(cleanup::<T> as usize as i32, state_ptr as i32);
113                    ic0::call_perform()
114                };
115
116                // 0 is a special error code meaning call succeeded.
117                if err_code != 0 {
118                    let result = Err((
119                        RejectionCode::from(err_code),
120                        "Couldn't send message".to_string(),
121                    ));
122                    state.result = Some(result.clone());
123                    return Poll::Ready(result);
124                }
125            }
126            state.waker = Some(context.waker().clone());
127            Poll::Pending
128        }
129    }
130}
131
132/// The callback from IC dereferences the future from a raw pointer, assigns the
133/// result and calls the waker. We cannot use a closure here because we pass raw
134/// pointers to the System and back.
135///
136/// # Safety
137///
138/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata.
139unsafe extern "C" fn callback<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
140    // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata.
141    let state = unsafe { Weak::from_raw(state_ptr) };
142    if let Some(state) = state.upgrade() {
143        // Make sure to un-borrow_mut the state.
144        {
145            state.write().unwrap().result = Some(match reject_code() {
146                RejectionCode::NoError => Ok(arg_data_raw()),
147                n => Err((n, reject_message())),
148            });
149        }
150        let w = state.write().unwrap().waker.take();
151        if let Some(waker) = w {
152            // This is all to protect this little guy here which will call the poll() which
153            // borrow_mut() the state as well. So we need to be careful to not double-borrow_mut.
154            waker.wake()
155        }
156    }
157}
158
159/// This function is called when [callback] was just called with the same parameter, and trapped.
160/// We can't guarantee internal consistency at this point, but we can at least e.g. drop mutex guards.
161/// Waker is a very opaque API, so the best we can do is set a global flag and proceed normally.
162///
163/// # Safety
164///
165/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata.
166unsafe extern "C" fn cleanup<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
167    // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata.
168    let state = unsafe { Weak::from_raw(state_ptr) };
169    if let Some(state) = state.upgrade() {
170        // We set the call result, even though it won't be read on the
171        // default executor, because we can't guarantee it was called on
172        // our executor. However, we are not allowed to inspect
173        // reject_code() inside of a cleanup callback, so always set the
174        // result to a reject.
175        //
176        // Borrowing does not trap - the rollback from the
177        // previous trap ensures that the RwLock can be borrowed again.
178        state.write().unwrap().result = Some(Err((RejectionCode::NoError, "cleanup".to_string())));
179        let w = state.write().unwrap().waker.take();
180        if let Some(waker) = w {
181            // Flag that we do not want to actually wake the task - we
182            // want to drop it *without* executing it.
183            ic_cdk_executor::CLEANUP.store(true, Ordering::Relaxed);
184            waker.wake();
185            ic_cdk_executor::CLEANUP.store(false, Ordering::Relaxed);
186        }
187    }
188}
189
190fn add_payment(payment: u128) {
191    if payment == 0 {
192        return;
193    }
194    let high = (payment >> 64) as u64;
195    let low = (payment & u64::MAX as u128) as u64;
196    // SAFETY: ic0.call_cycles_add128 is always safe to call.
197    unsafe {
198        ic0::call_cycles_add128(high as i64, low as i64);
199    }
200}
201
202/// Sends a one-way message with `payment` cycles attached to it that invokes `method` with
203/// arguments `args` on the principal identified by `id`, ignoring the reply.
204///
205/// Returns `Ok(())` if the message was successfully enqueued, otherwise returns a reject code.
206///
207/// # Notes
208///
209///   * The caller has no way of checking whether the destination processed the notification.
210///     The system can drop the notification if the destination does not have resources to
211///     process the message (for example, if it's out of cycles or queue slots).
212///
213///   * The callee cannot tell whether the call is one-way or not.
214///     The callee must produce replies for all incoming messages.
215///
216///   * It is safe to upgrade a canister without stopping it first if it sends out *only*
217///     one-way messages.
218///
219///   * If the payment is non-zero and the system fails to deliver the notification, the behaviour
220///     is unspecified: the funds can be either reimbursed or consumed irrevocably by the IC depending
221///     on the underlying implementation of one-way calls.
222pub fn notify_with_payment128<T: ArgumentEncoder>(
223    id: Principal,
224    method: &str,
225    args: T,
226    payment: u128,
227) -> Result<(), RejectionCode> {
228    let args_raw = encode_args(args).expect("failed to encode arguments");
229    notify_raw(id, method, &args_raw, payment)
230}
231
232/// Like [notify_with_payment128], but sets the payment to zero.
233pub fn notify<T: ArgumentEncoder>(
234    id: Principal,
235    method: &str,
236    args: T,
237) -> Result<(), RejectionCode> {
238    notify_with_payment128(id, method, args, 0)
239}
240
241/// Like [notify], but sends the argument as raw bytes, skipping Candid serialization.
242pub fn notify_raw(
243    id: Principal,
244    method: &str,
245    args_raw: &[u8],
246    payment: u128,
247) -> Result<(), RejectionCode> {
248    let callee = id.as_slice();
249    // We set all callbacks to -1, which is guaranteed to be invalid callback index.
250    // The system will still deliver the reply, but it will trap immediately because the callback
251    // is not a valid function. See
252    // https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters#one-way-calls
253    // for more context.
254
255    // SAFETY:
256    // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
257    // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
258    // -1, i.e. usize::MAX, is a function pointer the wasm module cannot possibly contain, and therefore can be passed as both reply and reject fn for ic0.call_new.
259    // Since the callback function will never be called, any value can be passed as its context parameter, and therefore -1 can be passed for those values.
260    // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
261    // ic0.call_perform is always safe to call.
262    let err_code = unsafe {
263        ic0::call_new(
264            callee.as_ptr() as i32,
265            callee.len() as i32,
266            method.as_ptr() as i32,
267            method.len() as i32,
268            /* reply_fun = */ -1,
269            /* reply_env = */ -1,
270            /* reject_fun = */ -1,
271            /* reject_env = */ -1,
272        );
273        add_payment(payment);
274        ic0::call_data_append(args_raw.as_ptr() as i32, args_raw.len() as i32);
275        ic0::call_perform()
276    };
277    match err_code {
278        0 => Ok(()),
279        c => Err(RejectionCode::from(c)),
280    }
281}
282
283/// Similar to `call`, but without serialization.
284pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
285    id: Principal,
286    method: &str,
287    args_raw: T,
288    payment: u64,
289) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
290    call_raw_internal(id, method, args_raw, payment.into())
291}
292
293/// Similar to `call128`, but without serialization.
294pub fn call_raw128<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
295    id: Principal,
296    method: &str,
297    args_raw: T,
298    payment: u128,
299) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
300    call_raw_internal(id, method, args_raw, payment)
301}
302
303fn call_raw_internal<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
304    id: Principal,
305    method: &str,
306    args_raw: T,
307    payment: u128,
308) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
309    let state = Arc::new(RwLock::new(CallFutureState {
310        result: None,
311        waker: None,
312        id,
313        method: method.to_string(),
314        arg: args_raw,
315        payment,
316    }));
317    CallFuture { state }
318}
319
320fn decoder_error_to_reject<T>(err: candid::error::Error) -> (RejectionCode, String) {
321    (
322        RejectionCode::CanisterError,
323        format!(
324            "failed to decode canister response as {}: {}",
325            std::any::type_name::<T>(),
326            err
327        ),
328    )
329}
330
331/// Performs an asynchronous call to another canister using the [System API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-call).
332///
333/// If the reply payload is not a valid encoding of the expected type `T`,
334/// the call results in [RejectionCode::CanisterError] error.
335///
336/// Note that the asynchronous call must be awaited
337/// in order for the inter-canister call to be made
338/// using the [System API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-call).
339pub fn call<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
340    id: Principal,
341    method: &str,
342    args: T,
343) -> impl Future<Output = CallResult<R>> + Send + Sync {
344    let args_raw = encode_args(args).expect("Failed to encode arguments.");
345    let fut = call_raw(id, method, args_raw, 0);
346    async {
347        let bytes = fut.await?;
348        decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
349    }
350}
351
352/// Performs an asynchronous call to another canister and pay cycles at the same time.
353///
354/// Note that the asynchronous call must be awaited
355/// in order for the inter-canister call to be made
356/// using the [System API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-call).
357pub fn call_with_payment<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
358    id: Principal,
359    method: &str,
360    args: T,
361    cycles: u64,
362) -> impl Future<Output = CallResult<R>> + Send + Sync {
363    let args_raw = encode_args(args).expect("Failed to encode arguments.");
364    let fut = call_raw(id, method, args_raw, cycles);
365    async {
366        let bytes = fut.await?;
367        decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
368    }
369}
370
371/// Performs an asynchronous call to another canister and pay cycles at the same time.
372///
373/// Note that the asynchronous call must be awaited
374/// in order for the inter-canister call to be made
375/// using the [System API](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-call).
376pub fn call_with_payment128<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
377    id: Principal,
378    method: &str,
379    args: T,
380    cycles: u128,
381) -> impl Future<Output = CallResult<R>> + Send + Sync {
382    let args_raw = encode_args(args).expect("Failed to encode arguments.");
383    let fut = call_raw128(id, method, args_raw, cycles);
384    async {
385        let bytes = fut.await?;
386        decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
387    }
388}
389
390/// Returns a result that maps over the call
391///
392/// It will be Ok(T) if the call succeeded (with T being the arg_data),
393/// and [reject_message()] if it failed.
394pub fn result<T: for<'a> ArgumentDecoder<'a>>() -> Result<T, String> {
395    match reject_code() {
396        RejectionCode::NoError => {
397            decode_args(&arg_data_raw()).map_err(|e| format!("Failed to decode arguments: {}", e))
398        }
399        _ => Err(reject_message()),
400    }
401}
402
403/// Returns the rejection code for the call.
404pub fn reject_code() -> RejectionCode {
405    // SAFETY: ic0.msg_reject_code is always safe to call.
406    let code = unsafe { ic0::msg_reject_code() };
407    RejectionCode::from(code)
408}
409
410/// Returns the rejection message.
411pub fn reject_message() -> String {
412    // SAFETY: ic0.msg_reject_msg_size is always safe to call.
413    let len: u32 = unsafe { ic0::msg_reject_msg_size() as u32 };
414    let mut bytes = vec![0u8; len as usize];
415    // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset
416    unsafe {
417        ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
418    }
419    String::from_utf8_lossy(&bytes).into_owned()
420}
421
422/// Rejects the current call with the message.
423pub fn reject(message: &str) {
424    let err_message = message.as_bytes();
425    // SAFETY: `err_message`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
426    unsafe {
427        ic0::msg_reject(err_message.as_ptr() as i32, err_message.len() as i32);
428    }
429}
430
431/// An io::Write for message replies.
432#[derive(Debug, Copy, Clone)]
433pub struct CallReplyWriter;
434
435impl std::io::Write for CallReplyWriter {
436    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
437        // SAFETY: buf, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply_data_append.
438        unsafe {
439            ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32);
440        }
441        Ok(buf.len())
442    }
443
444    fn flush(&mut self) -> std::io::Result<()> {
445        Ok(())
446    }
447}
448
449/// Replies to the current call with a candid argument.
450pub fn reply<T: ArgumentEncoder>(reply: T) {
451    write_args(&mut CallReplyWriter, reply).expect("Could not encode reply.");
452    // SAFETY: ic0.msg_reply is always safe to call.
453    unsafe {
454        ic0::msg_reply();
455    }
456}
457
458/// Returns the amount of cycles that were transferred by the caller
459/// of the current call, and is still available in this message.
460pub fn msg_cycles_available() -> u64 {
461    // SAFETY: ic0.msg_cycles_available is always safe to call.
462    unsafe { ic0::msg_cycles_available() as u64 }
463}
464
465/// Returns the amount of cycles that were transferred by the caller
466/// of the current call, and is still available in this message.
467pub fn msg_cycles_available128() -> u128 {
468    let mut recv = 0u128;
469    // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128
470    unsafe {
471        ic0::msg_cycles_available128(&mut recv as *mut u128 as i32);
472    }
473    recv
474}
475
476/// Returns the amount of cycles that came back with the response as a refund.
477///
478/// The refund has already been added to the canister balance automatically.
479pub fn msg_cycles_refunded() -> u64 {
480    // SAFETY: ic0.msg_cycles_refunded is always safe to call
481    unsafe { ic0::msg_cycles_refunded() as u64 }
482}
483
484/// Returns the amount of cycles that came back with the response as a refund.
485///
486/// The refund has already been added to the canister balance automatically.
487pub fn msg_cycles_refunded128() -> u128 {
488    let mut recv = 0u128;
489    // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128
490    unsafe {
491        ic0::msg_cycles_refunded128(&mut recv as *mut u128 as i32);
492    }
493    recv
494}
495
496/// Moves cycles from the call to the canister balance.
497///
498/// The actual amount moved will be returned.
499pub fn msg_cycles_accept(max_amount: u64) -> u64 {
500    // SAFETY: ic0.msg_cycles_accept is always safe to call.
501    unsafe { ic0::msg_cycles_accept(max_amount as i64) as u64 }
502}
503
504/// Moves cycles from the call to the canister balance.
505///
506/// The actual amount moved will be returned.
507pub fn msg_cycles_accept128(max_amount: u128) -> u128 {
508    let high = (max_amount >> 64) as u64;
509    let low = (max_amount & u64::MAX as u128) as u64;
510    let mut recv = 0u128;
511    // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128
512    unsafe {
513        ic0::msg_cycles_accept128(high as i64, low as i64, &mut recv as *mut u128 as i32);
514    }
515    recv
516}
517
518/// Returns the argument data as bytes.
519pub fn arg_data_raw() -> Vec<u8> {
520    // SAFETY: ic0.msg_arg_data_size is always safe to call.
521    let len: usize = unsafe { ic0::msg_arg_data_size() as usize };
522    let mut bytes = Vec::with_capacity(len);
523    // SAFETY:
524    // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset
525    // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len.
526    unsafe {
527        ic0::msg_arg_data_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
528        bytes.set_len(len);
529    }
530    bytes
531}
532
533/// Gets the len of the raw-argument-data-bytes.
534pub fn arg_data_raw_size() -> usize {
535    // SAFETY: ic0.msg_arg_data_size is always safe to call.
536    unsafe { ic0::msg_arg_data_size() as usize }
537}
538
539/// Replies with the bytes passed
540pub fn reply_raw(buf: &[u8]) {
541    if !buf.is_empty() {
542        // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
543        unsafe { ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32) }
544    };
545    // SAFETY: ic0.msg_reply is always safe to call.
546    unsafe { ic0::msg_reply() };
547}
548
549/// Returns the argument data in the current call. Traps if the data cannot be
550/// decoded.
551pub fn arg_data<R: for<'a> ArgumentDecoder<'a>>() -> R {
552    let bytes = arg_data_raw();
553
554    match decode_args(&bytes) {
555        Err(e) => trap(&format!("failed to decode call arguments: {:?}", e)),
556        Ok(r) => r,
557    }
558}
559
560/// Accepts the ingress message.
561pub fn accept_message() {
562    // SAFETY: ic0.accept_message is always safe to call.
563    unsafe {
564        ic0::accept_message();
565    }
566}
567
568/// Returns the name of current canister method.
569pub fn method_name() -> String {
570    // SAFETY: ic0.msg_method_name_size is always safe to call.
571    let len: u32 = unsafe { ic0::msg_method_name_size() as u32 };
572    let mut bytes = vec![0u8; len as usize];
573    // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy
574    unsafe {
575        ic0::msg_method_name_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
576    }
577    String::from_utf8_lossy(&bytes).into_owned()
578}
579
580/// Gets the value of specified performance counter
581///
582/// See [`crate::api::performance_counter`].
583#[deprecated(
584    since = "0.11.3",
585    note = "This method conceptually doesn't belong to this module. Please use `ic_cdk::api::performance_counter` instead."
586)]
587pub fn performance_counter(counter_type: u32) -> u64 {
588    // SAFETY: ic0.performance_counter is always safe to call.
589    unsafe { ic0::performance_counter(counter_type as i32) as u64 }
590}
591
592/// Pretends to have the Candid type `T`, but unconditionally errors
593/// when serialized.
594///
595/// Usable, but not required, as metadata when using `#[query(manual_reply = true)]`,
596/// so an accurate Candid file can still be generated.
597#[derive(Debug, Copy, Clone, Default)]
598pub struct ManualReply<T: ?Sized>(PhantomData<T>);
599
600impl<T: ?Sized> ManualReply<T> {
601    /// Constructs a new `ManualReply`.
602    #[allow(clippy::self_named_constructors)]
603    pub const fn empty() -> Self {
604        Self(PhantomData)
605    }
606    /// Replies with the given value and returns a new `ManualReply`,
607    /// for a useful reply-then-return shortcut.
608    pub fn all<U>(value: U) -> Self
609    where
610        U: ArgumentEncoder,
611    {
612        reply(value);
613        Self::empty()
614    }
615    /// Replies with a one-element tuple around the given value and returns
616    /// a new `ManualReply`, for a useful reply-then-return shortcut.
617    pub fn one<U>(value: U) -> Self
618    where
619        U: CandidType,
620    {
621        reply((value,));
622        Self::empty()
623    }
624
625    /// Rejects the call with the specified message and returns a new
626    /// `ManualReply`, for a useful reply-then-return shortcut.
627    pub fn reject(message: impl AsRef<str>) -> Self {
628        reject(message.as_ref());
629        Self::empty()
630    }
631}
632
633impl<T> CandidType for ManualReply<T>
634where
635    T: CandidType + ?Sized,
636{
637    fn _ty() -> candid::types::Type {
638        T::_ty()
639    }
640    /// Unconditionally errors.
641    fn idl_serialize<S>(&self, _: S) -> Result<(), S::Error>
642    where
643        S: candid::types::Serializer,
644    {
645        Err(S::Error::custom("`Empty` cannot be serialized"))
646    }
647}