Rust で Firefox Add-on と Native messaging

##環境
Firefox 65.0
toolchain: stable-i686-pc-windows-msvc
rustc 1.32.0 (9fda7c223 2019-01-16)

##目的
[MDN にある Native messaging の Python による ping_pong サンプル](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Native_messaging)を Rust で実装してみます。Windows 環境での例です。

##ネイティブアプリ側
[MDN の説明にある通り](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#App_side)標準入出力を使ってメッセージのやり取りを行います。それぞれのメッセージの先頭 4bytes がメッセージ長になってます。Windows の場合はリトルエンディアンなので下記のようなメッセージ構造になります。

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 6 | 0 | 0 | 0 | " | p | i | n | g | " |

まずはメッセージ長を取得する処理です。`vec![0u8; 4]`で 4bytes 分のバッファを確保して、`read_exact`で読み込み、`copy_nonoverlapping`で`u32`に変換という流れです。`dst.to_le()`は書かなくても良いですが、リトルエンディアンということが分かるように書いてます。

```rust
pub fn read_size() -> io::Result<usize> {
    let mut buf = vec![0u8; 4];
    io::stdin().read_exact(buf.as_mut_slice())?;
    let size = unsafe {
        let mut dst: u32 = 0;
        ptr::copy_nonoverlapping(buf.as_ptr(),
                                 &mut dst as *mut u32 as *mut u8, 4);
        dst.to_le() as usize
    };
    Ok(size)
}
```

メッセージ長が取得できたのでメッセージ本文を読み込みます。`vec![0u8; size]`でメッセージ長分のバッファを確保して読み込み、`String::from_utf8_lossy()`で Rust の世界の文字列に変換します。ここでは読み込んだ文字列をコールバック関数に渡しています。

```rust
pub fn read_content(callback: Option<CB>) -> io::Result<()> {
    let size = read_size()?;

    let mut buf = vec![0u8; size];
    io::stdin().read_exact(buf.as_mut_slice())?;

    let content = String::from_utf8_lossy(buf.as_slice());
    if callback.is_some() {
        (callback.unwrap())(content.as_ref());
    }
    Ok(())
}
```

コールバック関数の型定義とメインループです。

```rust
type CB = fn(value: &str);

pub fn main_loop(callback: Option<CB>) {
    loop {
        read_content(callback).unwrap();
    }
}
```

入力側の処理は以上です。

次は出力側の処理を書いていきます。アプリ側からの送信するメッセージも先頭 4bytes がメッセージ長で、それにメッセージ本文が続きます。

まずはメッセージ長を出力します。アプリ側からのメッセージ送信は 1MiB に制限されているので、それを超えていたらエラーを返すようにしています。その次が`u32`を`[u8; 4]`に変換する処理です。

```rust
pub fn write_size(size: usize) -> io::Result<()> {
    if size > 1024 * 1024 {
        return Err(io::Error::from(io::ErrorKind::InvalidData));
    }
    let size: [u8; 4] = unsafe {
        *(&(size as u32).to_le() as *const u32 as *const [u8; 4])
    };
    let _ = io::stdout().write(&size)?;
    Ok(())
}
```

メッセージ本文を出力する処理です。

```rust
pub fn write_content(content: &str) -> io::Result<()> {
    let size = content.len();
    write_size(size)?;

    io::stdout().write(content.as_bytes())?;
    io::stdout().flush()?;
    Ok(())
}
```

以上を native_messaging module にして、main.rs から呼ぶようにしています。native_messaging.rs と main.rs をまとめてどうぞ。

```rust:native_messaging.rs
use std::io::{self, Read, Write};
use std::ptr;

type CB = fn(value: &str);

pub fn main_loop(callback: Option<CB>) {
    loop {
        read_content(callback).unwrap();
    }
}

pub fn read_content(callback: Option<CB>) -> io::Result<()> {
    let size = read_size()?;

    let mut buf = vec![0u8; size];
    io::stdin().read_exact(buf.as_mut_slice())?;

    let content = String::from_utf8_lossy(buf.as_slice());
    if callback.is_some() {
        (callback.unwrap())(content.as_ref());
    }
    Ok(())
}

pub fn read_size() -> io::Result<usize> {
    let mut buf = vec![0u8; 4];
    io::stdin().read_exact(buf.as_mut_slice())?;
    let size = unsafe {
        let mut dst: u32 = 0;
        ptr::copy_nonoverlapping(buf.as_ptr(),
                                 &mut dst as *mut u32 as *mut u8, 4);
        dst.to_le() as usize
    };
    Ok(size)
}

pub fn write_size(size: usize) -> io::Result<()> {
    if size > 1024 * 1024 {
        return Err(io::Error::from(io::ErrorKind::InvalidData));
    }
    let size: [u8; 4] = unsafe {
        *(&(size as u32).to_le() as *const u32 as *const [u8; 4])
    };
    let _ = io::stdout().write(&size)?;
    Ok(())
}

pub fn write_content(content: &str) -> io::Result<()> {
    let size = content.len();
    write_size(size)?;

    io::stdout().write(content.as_bytes())?;
    io::stdout().flush()?;
    Ok(())
}
```

main.rs はメインループを実行して、メッセージを受け取ったら、コールバック関数で "pong" というメッセージを返すだけです。

```rust:main.rs
mod native_messaging;
use native_messaging::{main_loop, write_content};

fn main() {
    main_loop(Some(callback));
}

fn callback(_content: &str) {
    write_content("pong").unwrap();
}
```

あくまでもサンプルなのでエラー処理はいい加減です。crates.io に [chrome_native_messaging](https://crates.io/crates/chrome_native_messaging) という crate があるのでそちらも参考にしてください。Firefox でも動くはず？

###manifest.json

以下の内容の manifest ファイルを ping_pong.exe と同じフォルダに保存します。

```json
{
  "name": "ping_pong",
  "description": "Example host for native messaging by Rust",
  "path": "ping_pong.exe",
  "type": "stdio",
  "allowed_extensions": [ "ping_pong@example.org" ]
}
```

この manifest ファイルへのパスを以下のようにレジストリに書き込みます。`C:¥path¥to¥manifest.json`のところは自分の環境に合わせて適宜書き換えてください。
![ping_pong.png](https://qiita-image-store.s3.amazonaws.com/0/353186/8ecd55dc-dafe-2e81-14cc-43178c58aea1.png)

##アドオン側
[MDN](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#Extension_side)とまったく同じなので（略