11
1

More than 5 years have passed since last update.

[Rust] Ironで1ファイルWebアプリを実現する

Last updated at Posted at 2017-10-18

IronでWebアプリ書いてクロスコンパイルして、ラズパイとかに送り込んで実行する際、jsやcssファイルが実行形式の中に入ってたらいいかも、ということで実際にやってみた。

追記:ほぼ同目的のパッケージがすでにcrate.ioに上がってました(https://crates.io/crates/include_dir)
後述してますが、Ironのハンドラまで含めて作ってみたので興味あれば使ってみてください。
https://crates.io/crates/embed_staticfile

ディレクトリに対してinclude_bytes!()を実行するコンパイラプラグインinclude_dir!()を作った

rust標準でinclude_bytes!()というコンパイラプラグインがある。
これは引数のファイルをコンパイル時に読み込んでバイト配列にしてくれるものである。
今回作ったマクロは引数ディレクトリ内ファイルのパスとバイト配列をHashMapに入れて返すinclude_dir!()というコンパイラプラグインである。

1ファイルWebアプリ実装

#![feature(plugin)]
#![plugin(include_dir)]

extern crate iron;
extern crate router;
extern crate mount;
extern crate conduit_mime_types as mime_types;
extern crate hyper;

#[macro_use] extern crate lazy_static;

use iron::prelude::*;
use iron::status;
use mount::Mount;
use std::collections::HashMap;
use std::path::Path;
use hyper::mime::Mime;

lazy_static! {
    static ref ASSETS: HashMap<String, Vec<u8>> = include_dir!("../assets");
    static ref MIME_TYPES: mime_types::Types = mime_types::Types::new().unwrap();
}

fn request_assets(req: &mut Request) -> IronResult<Response> {
    let path:String = req.url.path().pop().unwrap().to_string();
    match ASSETS.get(&path) {
        Some(b) => {
            let mut res = Response::with((status::Ok, b.as_slice()));

            let mime_str = MIME_TYPES.mime_for_path(Path::new(&path));
            let _ = mime_str.parse().map(|mime: Mime| res.set_mut(mime));
            Ok(res)
        },
        None => {
            Ok(Response::with((status::NotFound)))
        }
    }
}

fn main() {
    let mut mount = Mount::new();

    mount.mount("/assets/", request_assets);

    Iron::new(mount).http("localhost:3000").unwrap();
}

ASSETSinclude_dir!()で作ったパス文字列とバイト配列のHashMapが格納されている。
request_assets()ではリクエストのパスがASSETSにあれば内容を返し、なければ404を返している。
MIME_TYPESはパスからContentTypeを判定するたしレスポンスにセットするために使用している。

ビルドするとassets以下が取り込まれた実行形式が作成される。

追記(2017/10/25): 上記コードは include_dir_bytes ver0.1でのみ有効。ver0.2からはHashMap<&'static Path, Vec<u8>>を返すようになった。

追記(2017/10/20): ハンドラ部分もlib化した

Cargo.tomlのdependenciesを追加して

[dependencies]
include_dir_bytes = "0.2"
embed_staticfile = "0.2"

次のように書ける

#![feature(plugin)]
#![plugin(include_dir_bytes)]

extern crate iron;
extern crate mount;

extern crate embed_staticfile;

use iron::prelude::*;
use mount::Mount;
use embed_staticfile::EmbedStatic;

fn main() {
    let mut mount = Mount::new();
    mount.mount("/assets/", EmbedStatic::new(include_dir!("../assets")));

    Iron::new(mount).http("localhost:3000").unwrap();
}

課題

cargo buildをassets以下の変更に対応させたい

小さなプロジェクトならcargo-docargo do clean, buildすいればいいか

11
1
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
11
1