2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

gotham のハンドラとして async fn を書く

2
Posted at
$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
$ cargo --version
cargo 1.39.0 (1c6ec66d5 2019-09-30)

gotham や hyper はまだ async/await ( futures 0.3 ) に対応していません。
そこで async fn を適切な形に整形する必要があります。

下記のように書けるようにします。

src/main.rs
pub type HandlerResult = Result<(State, Response<Body>), (State, HandlerError)>;

# [compat_handler]
async fn piyo_handler(mut state: State) -> HandlerResult {
  let piyo = extract_json::<NewPiyo>(&mut state).compat().await;
  let body = serde_json::to_string(&piyo).expect("Failed to serialise to json");
  let res = create_response(&state, StatusCode::OK, mime::APPLICATION_JSON, body);
  Ok((state, res))
}

fn router() -> Router {
  let (chain, pipeline) = single_pipeline(new_pipeline().build());
  build_router(chain, pipeline, |route| {
    route.post("/piyo").to(piyo_handler);
  })
}

extract_json の定義は gotham のレポジトリにあった例から貰っています。

gotham が対応している非同期ハンドラ

gotham のレポジトリにあったこちらの例によれば

(mut state: gotham::state::State) -> Box<gotham::handler::HandlerFuture>

であるハンドラには対応しているようです。
futures 0.3 の互換性レイヤーを利用して下記のように書くことができます。

// futures = { version = "0.3", features = ["compat"] }
use futures::compat::Future01CompatExt;
use futures::future::{FutureExt, TryFutureExt};

fn piyo_handler(mut state: State) -> Box<HandlerFuture> {
  
  async fn piyo_handler(mut state: State) -> Result<(State, Response<Body>), (State, HandlerError)> {
    // 省略
    Ok((state, res))
  }

  #[allow(deprecated)]
  piyo_handler(state).boxed().compat().boxed()
}

属性マクロの作成

上記の書き方では少しコードが長くなってしまうため属性マクロを作成します。

新しくディレクトリを切ります。

compat_handler/Cargo.toml
[lib]
proc-macro = true
compat_handler/src/lib.rs
extern crate proc_macro;

use proc_macro::{TokenStream, TokenTree};

# [proc_macro_attribute]
pub fn compat_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
  let tokens: Vec<TokenTree> = item.clone().into_iter().collect();
  if "async" != tokens[0].to_string() {
    panic!("compat_handler require async fn");
  }
  let fnname = tokens[2].to_string();
  let code = item.to_string();
  let s = format!(
    r#"
fn {}(mut state: State) -> Box<HandlerFuture> {{
  {}
  #[allow(deprecated)]
  {}(state).boxed().compat().boxed()
}}
"#,
    fnname, code, fnname
  );
  // println!("{}", s);
  s.parse().expect("failed to parse")
}

use 宣言の追加

Cargo.toml
[dependencies]
gotham = "0.4.0"
gotham_derive = "0.4.0"
hyper = "0.12"
futures = { version = "0.3", features = ["compat"] }
futures01 = "0.1"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
chrono = { version = "0.4.9", features = ["serde"] }
+compat_handler = { path = "./compat_handler" }
src/main.rs
+use compat_handler::compat_handler;

use futures::compat::Future01CompatExt;
use futures::future::{FutureExt, TryFutureExt};
use futures01::{future, Future, Stream};
use gotham::handler::{HandlerError, HandlerFuture, IntoHandlerError};
use gotham::helpers::http::response::create_response;
use gotham::pipeline::{new_pipeline, single::single_pipeline};
use gotham::router::{builder::*, Router};
use gotham::state::{FromState, State};
use hyper::{Body, Response, StatusCode};
use serde_derive::Serialize;
use std::str::from_utf8;

参考にした記事

Rust の非同期処理はランタイムが標準ではいっていないなどつまずくところが多かったです。
下記の記事がとても分かりやすく理解の助けになりました。

Rustの非同期プログラミングをマスターする
Rustの未来いわゆるFuture

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?