shioとmysql_asyncでAPIサーバーを作ってみる

この記事は[Wanoグループ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/wano-group)の21日目の記事です。

今回は`shio-rs`と`mysql_async`を使ってAPIサーバーを構築してみたいと思います。


## shioとは

[tokio](https://tokio.rs/)という非同期IOによるネットワーク通信処理を扱う為のライブラリをベースにしたマイクロフレームワークです。

https://github.com/mehcode/shio-rs


## mysql_asyncとは

`shio`と同様に`tokio`をベースにしたMySQLクライアントライブラリです。
個人的に期待しているライブラリなのですが、ググってもあまり話が出てきません。


## 雛形のHello Worldを作成

まずはDBへの通信関係の処理は置いといてHello Worldを作ってみましょう
まずは`cargo new hello_shio --bin`等として空のcrateを作成します。
それから依存crate(=パッケージ)を`Cargo.toml`に次の様に追記します。

```toml
[dependenceis]
shio = "*"
```

`main.rs`は次の様になります。

```rust
extern crate shio;

use shio::prelude::*;

fn hello_world_handler(ctx: Context) -> Response {
    Response::with("Hello World")
}

fn main() {
    shio::default()
        .route((Method::Get, "/", hello_world_handler))
        .run("0.0.0.0:8080")
        .unwrap();
}
```

コードも短くて簡単ですね


## JSONの返し方を確認

次はJSONの返し方を確認しましょう。

まずはJSONを扱えるようにする為に`Cargo.toml`に次の依存crateを追加します。

```toml
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
error-chain = "*"
```

`serde`はシリアライズ・デシリアライズ処理の抽象化を行うライブラリ、
`serde_derive`はボイラープレートを削減する為のマクロを提供するライブラリ、
`serde_json`はその上に構築されたJSONを扱うライブラリになります。
`error-chain`は各種ライブラリの返すエラー型を束ねて一つのエラー型にまとめるのに使用します。

`main.rs`は次の様になります。

```rust
extern crate shio;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate error_chain;

mod errors {
    error_chain! {
        foreign_links {
            Json(::serde_json::Error);
        }
    }
}

use shio::prelude::*;
use shio::header::ContentType;

#[derive(Debug, Serialize, Deserialize)]
struct HelloWorld {
    message: String,
}

fn hello_world_handler(ctx: Context) -> Result<Response, errors::Error> {
    let hw = HelloWorld {
        message: "Hello World!".to_string(),
    };
    Ok(Response::build().header(ContentType::json()).body(serde_json::to_string(&hw)?))
}

fn main() {
    Shio::default()
        .route((Method::Get, "/", hello_world_handler))
        .run("0.0.0.0:8080")
        .unwrap();
}
```

変更した個所を順に見ていきます。

```rust
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate error_chain;
```

まず`Cargo.toml`に追記したcrateを`extern crate`で読み込みます。
`#[macro_use]`というのはcrate内で定義されているマクロを読み込む為の呪文です。

```rust
mod errors {
    error_chain! {
        foreign_links {
            Json(::serde_json::Error);
        }
    }
}
```

`serde_json`の関数が返すエラー型をそのまま扱うのではなく、このプログラム用のエラー型(`errors::Error`型)を定義する為にまたもや呪文を書きます。
この呪文によって`serde_json`が返すエラー型をこのプログラム用のエラー型に変換する事が出来るようになります(`From` traitの自動実装が行われています)。
ここでは`error-chain`を使っていますが、`quick-error`を使ったり、面倒くさいですが手で直接定義してもいいです。

```rust
use shio::prelude::*;
use shio::header::ContentType;
```

後の行で`ContentType`型を使用するのでここで`use`して名前空間にインポートしています。
JSONのレスポンスを生成する部分を関数にして、その中でだけ`use`してもいいかもしれません。

```rust
#[derive(Debug, Serialize, Deserialize)]
struct HelloWorld {
    message: String,
}
```

レスポンスとして返すJSON用の型を定義しています。
`#[derive(Debug, Serialize, Deserialize)]`はマクロで、ここでは`Debug`, `Serialize`, `Deserialize` traitの自動実装を行っています。
また、ここでは話を簡単にする為に`message: String`という様に`&'a str`ではなく`String`型を用いています。

```rust
fn hello_world_handler(ctx: Context) -> Result<Response, errors::Error> {
    let hw = HelloWorld {
        message: "Hello World!".to_string(),
    };
    Ok(Response::build().header(ContentType::json()).body(serde_json::to_string(&hw)?))
}
```

レスポンスとしてJSONを返す為に`hello_world_handler`の中がガラッと変わりました。
JSONを返す為、`Response::build()`で`Builder`を取得して`header()`で`Content-Type`をセットして、最後に`body()`でJSON文字列をセットして`Response`を作ります。

`serde_json::to_string()`が`Result`を返しますが、ここでは`Err`であれば即`return`するだけなので`?`オペレータを使って`match`等をするコードを省略します。この時に上の方で`error_chain!`を使って定義したエラー型に自動で変換してくれています。

これで後から拡張出来そうな感じにレスポンスをJSONで返すことが出来るようになりました。


## POSTでJSONやGETクエリを受け取る

POSTされたJSONやGETクエリを受け取れるようにしてみましょう

まずは全体像はこちらです。

Cargo.toml

```toml
[package]
name = "my-api-server"
version = "0.1.0"
authors = ["you <your@mailaddress>"]

[dependencies]
shio = "*"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
error-chain = "*"
queryst = "1.0"
futures = "*"
hyper = "0.11"
```

main.rs

```rust
extern crate shio;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate error_chain;
extern crate queryst;
extern crate futures;
extern crate hyper;

pub mod errors {
    error_chain! {
        foreign_links {
            Json(::serde_json::Error);
            Hyper(::hyper::Error);
        }

        errors {
            Queryst(e: ::queryst::ParseError) {
                description("queryst parse error")
                display("queryst parse error: {:?}", e)
            }
        }
    }
}

use futures::prelude::*;
use shio::prelude::*;
use shio::header::ContentType;

#[derive(Debug, Serialize, Deserialize)]
struct HelloWorld {
    message: String,
}

fn hello_world_handler(ctx: Context) -> Result<Response, errors::Error> {
    let hw = HelloWorld {
        message: "Hello World!".to_string(),
    };
    Ok(Response::build().header(ContentType::json()).body(serde_json::to_string(&hw)?))
}

fn query_string_handler(ctx: Context) -> Result<Response, errors::Error> {
    if let Some(query_string) = ctx.uri().query() {
        let value = queryst::parse(query_string).map_err(|e| errors::ErrorKind::Queryst(e))?;
        let msg = value.find("message").and_then(|v| v.as_str()).unwrap_or("No Message");
        return Ok(Response::with(msg));
    }

    Ok(Response::with("No Query"))
}

fn post_json_handler(ctx: Context) -> Box<Future<Item = Response, Error = errors::Error>> {
    let future = ctx.body()
        .from_err::<errors::Error>()
        .concat2()
        .and_then(|body| {
            let v: serde_json::Value = serde_json::from_slice(&body)?;
            let op_first_name = v.pointer("/profile/first_name").and_then(|v| v.as_str());
            let op_last_name = v.pointer("/profile/last_name").and_then(|v| v.as_str());
            if let (Some(first_name), Some(last_name)) = (op_first_name, op_last_name) {
                Ok(Response::with(format!("Hello {} {}", first_name, last_name)))
            } else {
                Ok(Response::with("Please include 'profile.first_name' and 'profile.last_name' key in json"))
            }
        });

    Box::new(future)
}

fn main() {
    Shio::default()
        .route((Method::Get, "/", hello_world_handler))
        .route((Method::Get, "/query_string", query_string_handler))
        .route((Method::Post, "/json", post_json_handler))
        .run("0.0.0.0:8080")
        .unwrap();
}
```

JSONを返すだけのプログラムから変わった個所を見ていきます。

```toml
queryst = "1.0"
futures = "*"
hyper = "0.11"
```

まずはCargo.tomlですが、3つ依存crateが増えています。
`queryst`はGETクエリをパースするのに使用します。
`futures`は非同期処理の為のTraitを提供するパッケージです。
`hyper`は`shio`のベースになっているライブラリで、今回使用するAPIの一部が`hyper`で定義されているエラー型を返すので使用します。

```rust
pub mod errors {
    error_chain! {
        foreign_links {
            Json(::serde_json::Error);
            Hyper(::hyper::Error);
        }

        errors {
            Queryst(e: ::queryst::ParseError) {
                description("queryst parse error")
                display("queryst parse error: {:?}", e)
            }
        }
    }
}
```

次にmain.rsですが、まず`errors`の中が変わっています。

`foreign_links`の中に`Hyper(::hyper::Error);`が追加されています。
これにより`::hyper::Error`型のエラーを`errors::Error`へ変換する事が出来るようになります。

さらに`errors`という項目も増えています。
`error_chain!`マクロは`ErrorKind`というenumを定義するのですが、ここに記述した物はその`ErrorKind`の値になります。
`ErrorKind`は`?`オペレータや`From`や`errors::Error::from_kind`で`errors::Error`型への変換が出来ます。
`queryst`のエラー型である`ParseError`が`std::error::Error`を実装しておらず`foreign_links`に書くことが出来ない為、こちらで定義して必要に応じて手動で変換していきます。

```rust
use futures::prelude::*;
use shio::prelude::*;
use shio::header::ContentType;
```

`use futures::prelude::*;`が増えました。
これによりfuturesで定義されている様々なtraitを簡単に利用できるようになります。

```rust
fn query_string_handler(ctx: Context) -> Result<Response, errors::Error> {
    if let Some(query_string) = ctx.uri().query() {
        let value = queryst::parse(query_string).map_err(|e| errors::ErrorKind::Queryst(e))?;
        let msg = value.find("message").and_then(|v| v.as_str()).unwrap_or("No Message");
        return Ok(Response::with(msg));
    }

    Ok(Response::with("No Query"))
}
```

GETクエリのパースのサンプル用に`query_string_handler`が追加されました。
ここでは`ctx.uri().query()`でクエリストリングを取得し、`queryst::parse(query_string)`でパースします。
`queryst::parse()`は`Result<Value, ParseError>`を返すので、`map_err`で`ParseError`を`ErrorKind`に変換します。

`Value`は`serde_json`で提供されている型です。
ここでは`find()`を使って適当なキーの値を取得して、それをそのままレスポンスとして返しています。

```rust
fn post_json_handler(ctx: Context) -> Box<Future<Item = Response, Error = errors::Error>> {
    let future = ctx.body()
        .from_err::<errors::Error>()
        .concat2()
        .and_then(|body| {
            let v: serde_json::Value = serde_json::from_slice(&body)?;
            let op_first_name = v.pointer("/profile/first_name").and_then(|v| v.as_str());
            let op_last_name = v.pointer("/profile/last_name").and_then(|v| v.as_str());
            if let (Some(first_name), Some(last_name)) = (op_first_name, op_last_name) {
                Ok(Response::with(format!("Hello {} {}", first_name, last_name)))
            } else {
                Ok(Response::with("Please include 'profile.first_name' and 'profile.last_name' key in json"))
            }
        });

    Box::new(future)
}
```

POSTでJSONを受け取るサンプルとして`post_json_handler()`が追加されました。

リクエストボディは`ctx.body()`で取得する事が出来ます。
そしてその直後に`from_err::<errors::Error>()`とする事でエラーの型を`errors::Error`へと変更しています。

`ctx.body()`は`Body`型を返しますが、この`Body`型にはリクエストボディを簡単に取れるようなメソッドがありません。
しかし代わりに`futures`で定義されている`Stream` traitを実装していますので、こちらのメソッドを使ってリクエストボディを取得します。
JSONは文字列全体を取得しないとパースが出来ないので、全体がまとまった状態で取得できる`concat2()`メソッドでリクエストボディ全体を取得します。

次に`concat2()`によって取得できたリクエストボディを`serde_json::from_slice()`によってパースします。

ここまで出来たら後は`query_string_handler()`と同様に`serde_json`から提供されている関数をやりたい処理に応じて使用していき、最後に型が合うように`Response`を返せばOKです。

こうやって構築した大きなFutureは型が複雑になってしまっています。
これを簡単に取り扱えるようにする為に最後に`Box::new(future)`でボックス化してTraitオブジェクトにしています。

```rust
fn main() {
    Shio::default()
        .route((Method::Get, "/", hello_world_handler))
        .route((Method::Get, "/query_string", query_string_handler))
        .route((Method::Post, "/json", post_json_handler))
        .run("0.0.0.0:8080")
        .unwrap();
}
```

`query_string_handler()`と`post_json_handler()`に対応するルーティングの設定が追加されました。


## DBからデータを取得

最後にDBとデータのやり取りをする部分を追加してそれっぽい感じにします。

全体は次の様になります。

Cargo.toml:

```toml
[package]
name = "my-api-server"
version = "0.1.0"
authors = ["you <your@mailaddress>"]

[dependencies]
shio = "*"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
error-chain = "*"
queryst = "1.0"
futures = "*"
hyper = "0.11"
chrono = { version = "0.4", features = ["serde"] }
mysql_async = "*"
tokio-core = "*"
```

main.rs:

```rust
extern crate shio;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate error_chain;
extern crate queryst;
extern crate futures;
extern crate hyper;
extern crate chrono;
#[macro_use]
extern crate mysql_async;
extern crate tokio_core;

pub mod errors {
    error_chain! {
        links {
            MySQL(::mysql_async::errors::Error, ::mysql_async::errors::ErrorKind);
        }

        foreign_links {
            Json(::serde_json::Error);
            Hyper(::hyper::Error);
            FromRow(::mysql_async::FromRowError);
        }

        errors {
            Queryst(e: ::queryst::ParseError) {
                description("queryst parse error")
                display("queryst parse error: {:?}", e)
            }
        }
    }
}

use futures::prelude::*;
use shio::prelude::*;
use shio::header::ContentType;
use tokio_core::reactor::Handle;
use chrono::prelude::*;
use mysql_async::prelude::*;
use mysql_async::{self as my, Pool, OptsBuilder, FromRowError};
use std::sync::RwLock;

thread_local!(static MYSQL_CONN_POOL: RwLock<Option<Pool>> = {
    RwLock::new(None)
});

fn get_pool(handle: &Handle) -> Pool {
    if let Some(pool) = MYSQL_CONN_POOL.with(|lock| lock.read().unwrap().clone()) {
        return pool;
    }

    MYSQL_CONN_POOL.with(|lock| {
        let mut pool = lock.write().unwrap();

        let mut builder = OptsBuilder::from_opts("mysql://root:piyo@172.17.0.2/employees");
        builder.pool_max(Some(20usize));

        let new_pool = Pool::new(builder, handle);
        *pool = Some(new_pool.clone());

        return new_pool;
    })
}

#[derive(Debug, Serialize, Deserialize)]
struct HelloWorld {
    message: String,
}

fn hello_world_handler(ctx: Context) -> errors::Result<Response> {
    let hw = HelloWorld {
        message: "Hello World!".to_string(),
    };
    Ok(Response::build().header(ContentType::json()).body(serde_json::to_string(&hw)?))
}

fn query_string_handler(ctx: Context) -> errors::Result<Response> {
    if let Some(query_string) = ctx.uri().query() {
        let value = queryst::parse(query_string).map_err(|e| errors::ErrorKind::Queryst(e))?;
        let msg = value.find("message").and_then(|v| v.as_str()).unwrap_or("No Message");
        return Ok(Response::with(msg));
    }

    Ok(Response::with("No Query"))
}

fn post_json_handler(ctx: Context) -> Box<Future<Item=Response, Error=errors::Error>> {
    let future = ctx.body()
        .from_err()
        .concat2()
        .and_then(|body| {
            let v: serde_json::Value = serde_json::from_slice(&body)?;
            let op_first_name = v.pointer("/profile/first_name").and_then(|v| v.as_str());
            let op_last_name = v.pointer("/profile/last_name").and_then(|v| v.as_str());
            if let (Some(first_name), Some(last_name)) = (op_first_name, op_last_name) {
                Ok(Response::with(format!("Hello {} {}", first_name, last_name)))
            } else {
                Ok(Response::with("Please include 'profile.first_name' and 'profile.last_name' key in json"))
            }
        });

    Box::new(future)
}

#[derive(Debug, Serialize, Deserialize)]
struct Employee {
    emp_no: i32,
    birth_date: NaiveDate,
    first_name: String,
    last_name: String,
    gender: String,
    hire_date: NaiveDate,
}

impl FromRow for Employee {
    fn from_row(row: my::Row) -> Employee {
        match my::from_row_opt(row) {
            Ok(v) => v,
            Err(err) => panic!("Convert row error: {:?} to the Employee", err),
        }
    }

    fn from_row_opt(row: my::Row) -> Result<Employee, FromRowError> {
        my::from_row_opt(row).map(|(emp_no, birth_date, first_name, last_name, gender, hire_date)| {
            Employee { emp_no, birth_date, first_name, last_name, gender, hire_date }
        })
    }
}

fn get_from_and_to_emp_no(req: &Request) -> errors::Result<(i64, i64)> {
    let default_from = 10000;
    let default_to = 11000;

    if let Some(query_string) = req.uri().query() {
        let value = queryst::parse(query_string).map_err(|e| errors::ErrorKind::Queryst(e))?;
        let from = value.find("from").and_then(|v| v.as_i64()).unwrap_or(default_from);
        let to = value.find("to").and_then(|v| v.as_i64()).unwrap_or(default_to);
        return Ok((from, to));
    } else {
        return Ok((default_from, default_to));
    }
}

fn employee_list_handler(ctx: Context) -> Box<Future<Item=Response, Error=errors::Error>> {
    let future = get_from_and_to_emp_no(&ctx)
        .into_future()
        .from_err()
        .and_then(move |(from_emp_no, to_emp_no)| {
            let pool = get_pool(ctx.handle());
            pool.get_conn()
                .from_err()
                .and_then(move |conn| {
                    conn.prep_exec(
                            r#"
                            SELECT
                                emp_no,
                                birth_date,
                                first_name,
                                last_name,
                                gender,
                                hire_date
                            FROM employees
                            WHERE emp_no >= :from AND emp_no <= :to;
                            "#,
                            params! {
                                "from" => from_emp_no,
                                "to" => to_emp_no,
                            },
                        )
                        .and_then(|result| {
                            result.collect_and_drop()
                        })
                        .from_err()
                })
                .and_then(|(_, employees): (_, Vec<Employee>)| {
                    let res = Response::build()
                        .header(ContentType::json())
                        .body(serde_json::to_string(&employees)?);
                    Ok(res)
                })
        });

    Box::new(future)
}

fn main() {
    Shio::default()
        .route((Method::Get, "/", hello_world_handler))
        .route((Method::Get, "/query_string", query_string_handler))
        .route((Method::Post, "/json", post_json_handler))
        .route((Method::Get, "/employees", employee_list_handler))
        .run("0.0.0.0:8080")
        .unwrap();
}
```

変わった部分を見ていきます。

```toml
chrono = { version = "0.4", features = ["serde"] }
mysql_async = "*"
tokio-core = "*"
```

`chrono`は時刻を扱うライブラリです。
featuresに`["serde"]`を設定して`serde`によるシリアライズ・デシリアライズが出来るようにします。

`mysql_async`はMySQLに非同期IOで接続するライブラリです。
`tokio`をベースに構築されています。

`tokio-core`は非同期IO処理を抽象化したライブラリです。
ここでは`tokio-core`で定義されている`Handle`型が必要なので依存crateに追加します。

```rust
extern crate chrono;
#[macro_use]
extern crate mysql_async;
extern crate tokio_core;
```

依存crateの追加に合わせて`extern crate`する行が増えました。

```rust
pub mod errors {
    error_chain! {
        links {
            MySQL(::mysql_async::errors::Error, ::mysql_async::errors::ErrorKind);
        }

        foreign_links {
            Json(::serde_json::Error);
            Hyper(::hyper::Error);
            FromRow(::mysql_async::FromRowError);
        }

        errors {
            Queryst(e: ::queryst::ParseError) {
                description("queryst parse error")
                display("queryst parse error: {:?}", e)
            }
        }
    }
}
```

`mysql_async`のエラー型を取り扱う為の記述が増えています。
`mysql_async::errors::Error`は`error-chain`によって定義されているので、`links`という項目に連携の為の設定を記述しています。
`links`は`error-chain`によって定義された他の`Error`型と連携させたいときに使用します。

```rust
use tokio_core::reactor::Handle;
use chrono::prelude::*;
use mysql_async::prelude::*;
use mysql_async::{self as my, Pool, OptsBuilder, FromRowError};
use std::sync::RwLock;
```

それぞれ後の処理で使用する型を名前空間にインポートしています。

```rust
thread_local!(static MYSQL_CONN_POOL: RwLock<Option<Pool>> = {
    RwLock::new(None)
});

fn get_pool(handle: &Handle) -> Pool {
    if let Some(pool) = MYSQL_CONN_POOL.with(|lock| lock.read().unwrap().clone()) {
        return pool;
    }

    MYSQL_CONN_POOL.with(|lock| {
        let mut pool = lock.write().unwrap();

        let mut builder = OptsBuilder::from_opts("mysql://root:piyo@172.17.0.2/employees");
        builder.pool_max(Some(20usize));

        let new_pool = Pool::new(builder, handle);
        *pool = Some(new_pool.clone());

        return new_pool;
    })
}
```

ここではスレッドローカルな変数として`mysql_async`のコネクションプールである`Pool`を生成しています。

`thread_local!`を使う事でスレッドローカルな変数を定義できます。
アクセスする時は`with`メソッド等を使います。
（詳しくはこちら https://doc.rust-lang.org/std/thread/struct.LocalKey.html ）

しかし`Pool`型は初期化に`&Handle`を要求してくるのですが、`shio`がスレッド毎に保持している`tokio_core::reactor::Core`の`Handle`は、`Context`を通してしか取得する事が出来ません。
その為、`Pool`の取得時にまだ初期化されていない場合は初期化するという処理を行う必要があります。
この時データレースを防ぐために`RwLock`を用いて初期化処理時にだけ排他ロックを掛けられるようにしています。

`Pool`は接続先を`new`に直接文字列を渡して設定する事もできるのですが、コネクション保持数の上限などをカスタマイズしたい場合は`OptsBuilder`を使用して設定します。


```rust
#[derive(Debug, Serialize, Deserialize)]
struct Employee {
    emp_no: i32,
    birth_date: NaiveDate,
    first_name: String,
    last_name: String,
    gender: String,
    hire_date: NaiveDate,
}

impl FromRow for Employee {
    fn from_row(row: my::Row) -> Employee {
        match my::from_row_opt(row) {
            Ok(v) => v,
            Err(err) => panic!("Convert row error: {:?} to the Employee", err),
        }
    }

    fn from_row_opt(row: my::Row) -> Result<Employee, FromRowError> {
        my::from_row_opt(row).map(|(emp_no, birth_date, first_name, last_name, gender, hire_date)| {
            Employee { emp_no, birth_date, first_name, last_name, gender, hire_date }
        })
    }
}
```

今回サンプルデータとして[MySQLのEmployeesデータセット](https://dev.mysql.com/doc/employee/en/)を使用します。
その中の`employees`テーブルの行に対応する構造体として`Employee`型を定義しています。
またクエリの結果から`Employee`型への変換方法を`FromRow`型を実装する事で定義しています。
`from_row`関数や`from_row_opt`関数を使ってタプルに変換するようにすると簡単に取り扱える為、ここではそのようにしてから`Employee`を生成しています。

`Employee`型は後でJSONとしてクライアントに返すので、`serde`の機能を使って`Serialize`と`Deserialize` traitの自動実装を行っています。

この辺りの実装は面倒なのでもっと簡単な方法は無いのでしょうか？
[diesel](http://diesel.rs/)がtokioに対応するまで待つしかないのかなぁ

```rust
fn employee_list_handler(ctx: Context) -> Box<Future<Item=Response, Error=errors::Error>> {
    let future = get_from_and_to_emp_no(&ctx)
        .into_future()
        .from_err()
        .and_then(move |(from_emp_no, to_emp_no)| {
            let pool = get_pool(ctx.handle());
            pool.get_conn()
                .from_err()
                .and_then(move |conn| {
                    conn.prep_exec(
                            r#"
                            SELECT
                                emp_no,
                                birth_date,
                                first_name,
                                last_name,
                                gender,
                                hire_date
                            FROM employees
                            WHERE emp_no >= :from AND emp_no <= :to;
                            "#,
                            params! {
                                "from" => from_emp_no,
                                "to" => to_emp_no,
                            },
                        )
                        .and_then(|result| {
                            result.collect_and_drop()
                        })
                        .from_err()
                })
                .and_then(|(_, employees): (_, Vec<Employee>)| {
                    let res = Response::build()
                        .header(ContentType::json())
                        .body(serde_json::to_string(&employees)?);
                    Ok(res)
                })
        });

    Box::new(future)
}
```

GETクエリで指定された範囲の従業員番号を持つ人のリストをJSONとして返すハンドラです。

GETクエリの処理は`get_from_and_to_emp_no`関数で行っています。
この関数の実装は前の方でGETクエリを処理したハンドラの実装とそれほど変わらないので説明は省略します。

`get_from_and_to_emp_no`関数は`Result`を返しますが、その後に`Future`による処理を続けたいので`into_future`メソッドで`FutureResult`に変換します。
そして`from_err`でエラー型を変換し、MySQLにクエリを投げる処理へと続きます。

少し前に定義した`get_pool`関数を使ってコネクションプールをスレッドローカルな変数から取得します。
このアクセスが初回である場合、コネクションプールの初期化が`get_pool`内で行われます。

後は`mysql_async`のドキュメントにあるコード例ほぼそのままのコードを書いて従業員のリストを取得し、その後それをJSONに変換してレスポンスとして返します。

こうやって構築された巨大なFutureは型が複雑で、そのまま戻り値の型を記述するととても長くなってしまって扱いにくいのでBox化してTraitオブジェクト化する事で扱いやすくします。


## 終わりに

以上で`shio`と`mysql_async`を用いた簡易APIサーバーが構築できました。
後はこれに認証処理を追加したり等いろいろ拡張していくとそれっぽい物が出来ていくと思います。

ただ、まだまだFuture周りはインデントが深くなりがちになってしまったり、変数の所有権の移動などでボローチェッカーから怒られやすかったりと、決して書きやすいとは言えない状況だと思います。
この辺りの問題についてはasync/awaitをマクロで実装したライブラリ( https://github.com/alexcrichton/futures-await )を使う事で軽減できるように見えますが、まだ使ったことがないので私からは何とも言えません。

tokioベースのライブラリはまだまだこれから発展していくところですので、今後に期待をして今回の記事を終わりたいと思います。