Repository: tauri
Issue: Deprecate JSON in IPC
Labels: type: feature request

### Discussed in https://github.com/tauri-apps/tauri/discussions/7699

<div type='discussions-op-text'>

<sup>Originally posted by **wyhaya** August 26, 2023</sup>
Currently in Tauri's ipc it is passed in `JSON` format, which can cause some problems.

### Number overflow

Since JSON doesn't support `BigInt`, JavaScript's `Number` doesn't represent numbers in Rust well, e.g. `u64`,`u128` etc.


```Rust
// This will get an unexpected result in JavaScript
#[command]
fn number() -> u64 {
  u64::MAX
}
```
u64::MAX = `18446744073709551615`, But in JavaScript we get `18446744073709552000`, To pass it safely, we have to convert it to String, which is not a good solution.

## Binary data

JSON doesn't support passing Bytes, currently Tauri converts it to a `JSON Array`, which I think adds some overhead.
For example if I want to pass a Vec<u8> as binary data, I convert it to `String` via `base64` and then decode it to `Uint8Array`  in JavaScript, and even then it's faster than Array.

```Rust
use base64::{engine::general_purpose, Engine as _};
#[command]
async fn test() -> Result<String, Error> {
    let bytes = vec![...] ;
    Ok(general_purpose::STANDARD_NO_PAD.encode(bytes))
}
```

## Tauri 2.0.0-alpha.11

I've noticed that the new version of tauri supports passing binary data directly to the corresponding `ArrayBuffer`, which is great, now we have unlimited possibilities.

https://github.com/tauri-apps/tauri/blob/af3268a4be69e2dee72ba867ee54bc36d12712e6/examples/commands/main.rs#L220-L224

But does this mean we have to manually convert `struct` to `Vec<u8>` and parse `ArrayBuffer` in Javascript?
If we only have a single value, this is perfectly fine, if we have multiple values, we have to manually serialise and deserialise?

Consider the following code:
```Rust
struct Data {
   xl: Vec<u8>,
   lg: Vec<u8>
}
// How do I respond to JavaScript with a `Response` and parse it in JavaScript?
```

## My thoughts
JSON is great in most cases, but in some cases it doesn't meet our needs, and passing `Response` directly is very efficient, but not convenient for developers.

We can write a new `RJON`(Rust JavaScript Object Notation) to replace JSON.
1. It is based on binary instead of text and will be faster in serialisation and deserialisation.
2. It supports more types in JavaScript and is safer without worrying about overflow.
3. It supports Bytes directly, without converting them to Array<Number>.
4. More. e.g `Datetime` `UUID`

A simple example:

```Rust
pub enum Value {
    Null,
    Bool(bool),
    I8(i8),
    U8(u8),
    I16(i16),
    U16(u16),
    I32(i32),
    U32(u32),
    F32(f32),
    I64(i64),
    U64(u64),
    F64(f64),
    String(String),
    Bytes(Bytes),
    Array(Vec<Value>),
    Map(HashMap<String, Value>),
}
```

```JavaScript
export type Value =
    | null
    | boolean
    | Number
    | BigInt
    | string
    | Uint8Array
    | Value[]
    | { [key: string]: Value }
```

We can now pass these values better

```Rust
struct Data {
    a: i32,    // -> Number
    b: u64,    // -> BigInt
    c: f32,    // -> Number
    d: bool,   // -> Boolean
    f: Vec<T>, // -> Array<T>
    g: Bytes,  // -> Uint8Array
}
```

## Bad

We have to implement `RJON` serialisation and deserialisation in both Rust and JavaScript, which adds some work, and adds some size (which should be small) since JSON is built-in in JavaScript and `RJON` is not.

## Other

It would also be nice to allow `custom` IPC to bypass JSON.

```Rust
 // Use Tauri default IPC
builder.setup_ipc(|val| {
    None
});

// or

// Use custom IPC format
builder.setup_ipc(|val| {
    let res = Response::new(to_bytes(&val));
    Some(res)
});
```
</div>