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/// Performs an asynchronous call to another canister and pay cycles at the same time.
284///
285/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
286///
287/// # Example
288///
289/// It can be called:
290///
291/// ```rust
292/// # use ic_cdk::api::call::call_raw;
293/// # fn callee_canister() -> candid::Principal { unimplemented!() }
294/// async fn call_add_user() -> Vec<u8>{
295/// call_raw(callee_canister(), "add_user", b"abcd", 1_000_000u64).await.unwrap()
296/// }
297/// ```
298pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
299 id: Principal,
300 method: &str,
301 args_raw: T,
302 payment: u64,
303) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
304 call_raw_internal(id, method, args_raw, payment.into())
305}
306
307/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
308///
309/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
310/// # Example
311///
312/// It can be called:
313///
314/// ```rust
315/// # use ic_cdk::api::call::call_raw128;
316/// # fn callee_canister() -> candid::Principal { unimplemented!() }
317/// async fn call_add_user() -> Vec<u8>{
318/// call_raw128(callee_canister(), "add_user", b"abcd", 1_000_000u128).await.unwrap()
319/// }
320/// ```
321pub fn call_raw128<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
322 id: Principal,
323 method: &str,
324 args_raw: T,
325 payment: u128,
326) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
327 call_raw_internal(id, method, args_raw, payment)
328}
329
330fn call_raw_internal<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
331 id: Principal,
332 method: &str,
333 args_raw: T,
334 payment: u128,
335) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
336 let state = Arc::new(RwLock::new(CallFutureState {
337 result: None,
338 waker: None,
339 id,
340 method: method.to_string(),
341 arg: args_raw,
342 payment,
343 }));
344 CallFuture { state }
345}
346
347fn decoder_error_to_reject<T>(err: candid::error::Error) -> (RejectionCode, String) {
348 (
349 RejectionCode::CanisterError,
350 format!(
351 "failed to decode canister response as {}: {}",
352 std::any::type_name::<T>(),
353 err
354 ),
355 )
356}
357
358/// Performs an asynchronous call to another canister.
359///
360/// # Example
361///
362/// Assuming that the callee canister has following interface:
363///
364/// ```text
365/// service : {
366/// add_user: (name: text) -> (nat64);
367/// }
368/// ```
369///
370/// It can be called:
371///
372/// ```rust
373/// # use ic_cdk::api::call::call;
374/// # fn callee_canister() -> candid::Principal { unimplemented!() }
375/// async fn call_add_user() -> u64 {
376/// let (user_id,) = call(callee_canister(), "add_user", ("Alice".to_string(),)).await.unwrap();
377/// user_id
378/// }
379/// ```
380///
381/// # Note
382///
383/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
384/// * The type annotation on return type is required. Or the return type can be inferred from the context.
385/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
386/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
387pub fn call<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
388 id: Principal,
389 method: &str,
390 args: T,
391) -> impl Future<Output = CallResult<R>> + Send + Sync {
392 let args_raw = encode_args(args).expect("Failed to encode arguments.");
393 let fut = call_raw(id, method, args_raw, 0);
394 async {
395 let bytes = fut.await?;
396 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
397 }
398}
399
400/// Performs an asynchronous call to another canister and pay cycles at the same time.
401///
402/// # Example
403///
404/// Assuming that the callee canister has following interface:
405///
406/// ```text
407/// service : {
408/// add_user: (name: text) -> (nat64);
409/// }
410/// ```
411///
412/// It can be called:
413///
414/// ```rust
415/// # use ic_cdk::api::call::call_with_payment;
416/// # fn callee_canister() -> candid::Principal { unimplemented!() }
417/// async fn call_add_user() -> u64 {
418/// let (user_id,) = call_with_payment(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u64).await.unwrap();
419/// user_id
420/// }
421/// ```
422///
423/// # Note
424///
425/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
426/// * The type annotation on return type is required. Or the return type can be inferred from the context.
427/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
428/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
429pub fn call_with_payment<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
430 id: Principal,
431 method: &str,
432 args: T,
433 cycles: u64,
434) -> impl Future<Output = CallResult<R>> + Send + Sync {
435 let args_raw = encode_args(args).expect("Failed to encode arguments.");
436 let fut = call_raw(id, method, args_raw, cycles);
437 async {
438 let bytes = fut.await?;
439 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
440 }
441}
442
443/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
444///
445/// # Example
446///
447/// Assuming that the callee canister has following interface:
448///
449/// ```text
450/// service : {
451/// add_user: (name: text) -> (nat64);
452/// }
453/// ```
454///
455/// It can be called:
456///
457/// ```rust
458/// # use ic_cdk::api::call::call_with_payment128;
459/// # fn callee_canister() -> candid::Principal { unimplemented!() }
460/// async fn call_add_user() -> u64 {
461/// let (user_id,) = call_with_payment128(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u128).await.unwrap();
462/// user_id
463/// }
464/// ```
465///
466/// # Note
467///
468/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
469/// * The type annotation on return type is required. Or the return type can be inferred from the context.
470/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
471/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
472pub fn call_with_payment128<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
473 id: Principal,
474 method: &str,
475 args: T,
476 cycles: u128,
477) -> impl Future<Output = CallResult<R>> + Send + Sync {
478 let args_raw = encode_args(args).expect("Failed to encode arguments.");
479 let fut = call_raw128(id, method, args_raw, cycles);
480 async {
481 let bytes = fut.await?;
482 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
483 }
484}
485
486/// Returns a result that maps over the call
487///
488/// It will be Ok(T) if the call succeeded (with T being the arg_data),
489/// and [reject_message()] if it failed.
490pub fn result<T: for<'a> ArgumentDecoder<'a>>() -> Result<T, String> {
491 match reject_code() {
492 RejectionCode::NoError => {
493 decode_args(&arg_data_raw()).map_err(|e| format!("Failed to decode arguments: {}", e))
494 }
495 _ => Err(reject_message()),
496 }
497}
498
499/// Returns the rejection code for the call.
500pub fn reject_code() -> RejectionCode {
501 // SAFETY: ic0.msg_reject_code is always safe to call.
502 let code = unsafe { ic0::msg_reject_code() };
503 RejectionCode::from(code)
504}
505
506/// Returns the rejection message.
507pub fn reject_message() -> String {
508 // SAFETY: ic0.msg_reject_msg_size is always safe to call.
509 let len: u32 = unsafe { ic0::msg_reject_msg_size() as u32 };
510 let mut bytes = vec![0u8; len as usize];
511 // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset
512 unsafe {
513 ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
514 }
515 String::from_utf8_lossy(&bytes).into_owned()
516}
517
518/// Rejects the current call with the message.
519pub fn reject(message: &str) {
520 let err_message = message.as_bytes();
521 // SAFETY: `err_message`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
522 unsafe {
523 ic0::msg_reject(err_message.as_ptr() as i32, err_message.len() as i32);
524 }
525}
526
527/// An io::Write for message replies.
528#[derive(Debug, Copy, Clone)]
529pub struct CallReplyWriter;
530
531impl std::io::Write for CallReplyWriter {
532 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
533 // SAFETY: buf, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply_data_append.
534 unsafe {
535 ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32);
536 }
537 Ok(buf.len())
538 }
539
540 fn flush(&mut self) -> std::io::Result<()> {
541 Ok(())
542 }
543}
544
545/// Replies to the current call with a candid argument.
546pub fn reply<T: ArgumentEncoder>(reply: T) {
547 write_args(&mut CallReplyWriter, reply).expect("Could not encode reply.");
548 // SAFETY: ic0.msg_reply is always safe to call.
549 unsafe {
550 ic0::msg_reply();
551 }
552}
553
554/// Returns the amount of cycles that were transferred by the caller
555/// of the current call, and is still available in this message.
556pub fn msg_cycles_available() -> u64 {
557 // SAFETY: ic0.msg_cycles_available is always safe to call.
558 unsafe { ic0::msg_cycles_available() as u64 }
559}
560
561/// Returns the amount of cycles that were transferred by the caller
562/// of the current call, and is still available in this message.
563pub fn msg_cycles_available128() -> u128 {
564 let mut recv = 0u128;
565 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128
566 unsafe {
567 ic0::msg_cycles_available128(&mut recv as *mut u128 as i32);
568 }
569 recv
570}
571
572/// Returns the amount of cycles that came back with the response as a refund.
573///
574/// The refund has already been added to the canister balance automatically.
575pub fn msg_cycles_refunded() -> u64 {
576 // SAFETY: ic0.msg_cycles_refunded is always safe to call
577 unsafe { ic0::msg_cycles_refunded() as u64 }
578}
579
580/// Returns the amount of cycles that came back with the response as a refund.
581///
582/// The refund has already been added to the canister balance automatically.
583pub fn msg_cycles_refunded128() -> u128 {
584 let mut recv = 0u128;
585 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128
586 unsafe {
587 ic0::msg_cycles_refunded128(&mut recv as *mut u128 as i32);
588 }
589 recv
590}
591
592/// Moves cycles from the call to the canister balance.
593///
594/// The actual amount moved will be returned.
595pub fn msg_cycles_accept(max_amount: u64) -> u64 {
596 // SAFETY: ic0.msg_cycles_accept is always safe to call.
597 unsafe { ic0::msg_cycles_accept(max_amount as i64) as u64 }
598}
599
600/// Moves cycles from the call to the canister balance.
601///
602/// The actual amount moved will be returned.
603pub fn msg_cycles_accept128(max_amount: u128) -> u128 {
604 let high = (max_amount >> 64) as u64;
605 let low = (max_amount & u64::MAX as u128) as u64;
606 let mut recv = 0u128;
607 // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128
608 unsafe {
609 ic0::msg_cycles_accept128(high as i64, low as i64, &mut recv as *mut u128 as i32);
610 }
611 recv
612}
613
614/// Returns the argument data as bytes.
615pub fn arg_data_raw() -> Vec<u8> {
616 // SAFETY: ic0.msg_arg_data_size is always safe to call.
617 let len: usize = unsafe { ic0::msg_arg_data_size() as usize };
618 let mut bytes = Vec::with_capacity(len);
619 // SAFETY:
620 // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset
621 // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len.
622 unsafe {
623 ic0::msg_arg_data_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
624 bytes.set_len(len);
625 }
626 bytes
627}
628
629/// Gets the len of the raw-argument-data-bytes.
630pub fn arg_data_raw_size() -> usize {
631 // SAFETY: ic0.msg_arg_data_size is always safe to call.
632 unsafe { ic0::msg_arg_data_size() as usize }
633}
634
635/// Replies with the bytes passed
636pub fn reply_raw(buf: &[u8]) {
637 if !buf.is_empty() {
638 // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
639 unsafe { ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32) }
640 };
641 // SAFETY: ic0.msg_reply is always safe to call.
642 unsafe { ic0::msg_reply() };
643}
644
645/// Returns the argument data in the current call. Traps if the data cannot be
646/// decoded.
647pub fn arg_data<R: for<'a> ArgumentDecoder<'a>>() -> R {
648 let bytes = arg_data_raw();
649
650 match decode_args(&bytes) {
651 Err(e) => trap(&format!("failed to decode call arguments: {:?}", e)),
652 Ok(r) => r,
653 }
654}
655
656/// Accepts the ingress message.
657pub fn accept_message() {
658 // SAFETY: ic0.accept_message is always safe to call.
659 unsafe {
660 ic0::accept_message();
661 }
662}
663
664/// Returns the name of current canister method.
665pub fn method_name() -> String {
666 // SAFETY: ic0.msg_method_name_size is always safe to call.
667 let len: u32 = unsafe { ic0::msg_method_name_size() as u32 };
668 let mut bytes = vec![0u8; len as usize];
669 // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy
670 unsafe {
671 ic0::msg_method_name_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
672 }
673 String::from_utf8_lossy(&bytes).into_owned()
674}
675
676/// Gets the value of specified performance counter
677///
678/// See [`crate::api::performance_counter`].
679#[deprecated(
680 since = "0.11.3",
681 note = "This method conceptually doesn't belong to this module. Please use `ic_cdk::api::performance_counter` instead."
682)]
683pub fn performance_counter(counter_type: u32) -> u64 {
684 // SAFETY: ic0.performance_counter is always safe to call.
685 unsafe { ic0::performance_counter(counter_type as i32) as u64 }
686}
687
688/// Pretends to have the Candid type `T`, but unconditionally errors
689/// when serialized.
690///
691/// Usable, but not required, as metadata when using `#[query(manual_reply = true)]`,
692/// so an accurate Candid file can still be generated.
693#[derive(Debug, Copy, Clone, Default)]
694pub struct ManualReply<T: ?Sized>(PhantomData<T>);
695
696impl<T: ?Sized> ManualReply<T> {
697 /// Constructs a new `ManualReply`.
698 #[allow(clippy::self_named_constructors)]
699 pub const fn empty() -> Self {
700 Self(PhantomData)
701 }
702 /// Replies with the given value and returns a new `ManualReply`,
703 /// for a useful reply-then-return shortcut.
704 pub fn all<U>(value: U) -> Self
705 where
706 U: ArgumentEncoder,
707 {
708 reply(value);
709 Self::empty()
710 }
711 /// Replies with a one-element tuple around the given value and returns
712 /// a new `ManualReply`, for a useful reply-then-return shortcut.
713 pub fn one<U>(value: U) -> Self
714 where
715 U: CandidType,
716 {
717 reply((value,));
718 Self::empty()
719 }
720
721 /// Rejects the call with the specified message and returns a new
722 /// `ManualReply`, for a useful reply-then-return shortcut.
723 pub fn reject(message: impl AsRef<str>) -> Self {
724 reject(message.as_ref());
725 Self::empty()
726 }
727}
728
729impl<T> CandidType for ManualReply<T>
730where
731 T: CandidType + ?Sized,
732{
733 fn _ty() -> candid::types::Type {
734 T::_ty()
735 }
736 /// Unconditionally errors.
737 fn idl_serialize<S>(&self, _: S) -> Result<(), S::Error>
738 where
739 S: candid::types::Serializer,
740 {
741 Err(S::Error::custom("`Empty` cannot be serialized"))
742 }
743}