Repository: tauri-apps/tauri
File: crates/tauri/src/ipc/channel.rs (Rust)

Code before:
      ))?;

      Ok(())
    })
  }
}

impl<'de> Deserialize<'de> for JavaScriptChannelId {
  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
  where
    D: Deserializer<'de>,
  {
    let value: String = Deserialize::deserialize(deserializer)?;
    Self::from_str(&value).map_err(|_| {
      serde::de::Error::custom(format!(
        "invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
      ))
    })
  }
}

impl<TSend> Channel<TSend> {
  /// Creates a new channel with the given message handler.
  pub fn new<F: Fn(InvokeResponseBody) -> crate::Result<()> + Send + Sync + 'static>(
    on_message: F,
  ) -> Self {
    Self::new_with_id(CHANNEL_COUNTER.fetch_add(1, Ordering::Relaxed), on_message)
  }

  fn new_with_id<F: Fn(InvokeResponseBody) -> crate::Result<()> + Send + Sync + 'static>(
    id: u32,
    on_message: F,
  ) -> Self {
    #[allow(clippy::let_and_return)]
    let channel = Self {
      id,
      on_message: Arc::new(on_message),
      phantom: Default::default(),
    };

    #[cfg(mobile)]
    crate::plugin::mobile::register_channel(Channel {
      id,
      on_message: channel.on_message.clone(),
      phantom: Default::default(),
    });

    channel
  }

  pub(crate) fn from_callback_fn<R: Runtime>(webview: Webview<R>, callback: CallbackFn) -> Self {

Review comment (suggestion):
@Legend-Master This is a BREAKING CHANGE.

I need to use channels to send binary data:

- <https://github.com/pytauri/pytauri/blob/c738a5949859455e89e8095df1d0726831c29f1a/crates/tauri-plugin-pytauri/guest-js/src/index.ts#L94-L97>
- <https://github.com/pytauri/pytauri/blob/c738a5949859455e89e8095df1d0726831c29f1a/crates/pytauri-core/src/ext_mod_impl/ipc.rs#L301>

Previously, the received data was an `ArrayBuffer`, but now it has become an `Array`.

Code after:
    let counter = Arc::new(AtomicUsize::new(0));
    let counter_clone = counter.clone();
    let webview_clone = webview.clone();

    Channel::new_with_id(
      callback_id,
      Box::new(move |body| {
        let current_index = counter.fetch_add(1, Ordering::Relaxed);

        if let Some(interceptor) = &webview.manager.channel_interceptor {
          if interceptor(&webview, callback_fn, current_index, &body) {
            return Ok(());
          }
        }

        match body {
          // Don't go through the fetch process if the payload is small
          InvokeResponseBody::Json(string) if string.len() < MAX_JSON_DIRECT_EXECUTE_THRESHOLD => {
            webview.eval(format!(
              "window['_{callback_id}']({{ message: {string}, index: {current_index} }})"
            ))?;
          }
          InvokeResponseBody::Raw(bytes) if bytes.len() < MAX_RAW_DIRECT_EXECUTE_THRESHOLD => {
            webview.eval(format!(
              "window['_{callback_id}']({{ message: {}, index: {current_index} }})",
              serde_json::to_string(&bytes)?,
            ))?;
          }
          // use the fetch API to speed up larger response payloads
          _ => {
            let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);

            webview
              .state::<ChannelDataIpcQueue>()
              .0
              .lock()
              .unwrap()
              .insert(data_id, body);

            webview.eval(format!(
              "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_{callback_id}']({{ message: response, index: {current_index} }})).catch(console.error)",
            ))?;
          }
        }

        Ok(())
      }),
      Some(Box::new(move || {
        let current_index = counter_clone.load(Ordering::Relaxed);
        let _ = webview_clone.eval(format!(
          "window['_{callback_id}']({{ end: true, index: {current_index} }})",