Skip to content

Instantly share code, notes, and snippets.

@ctsrc
Last active April 6, 2018 14:26
Show Gist options
  • Save ctsrc/4c4cc05254d12bbc8937a0ea385fcdad to your computer and use it in GitHub Desktop.
Save ctsrc/4c4cc05254d12bbc8937a0ea385fcdad to your computer and use it in GitHub Desktop.
Single-binary website in Rust
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#case-study-code-generation
fn static_assets (mut f_dest: File, const_name: &str, root_dir: &Path, relpath_asset: &str)
{
f_dest.write_all(b"static ").unwrap();
f_dest.write_all(const_name.as_bytes()).unwrap();
f_dest.write_all(b": [&'static [u8]; 1] = [include_bytes!(\"").unwrap();
f_dest.write_all(root_dir.join(Path::new(relpath_asset)).as_path().to_str().unwrap().as_bytes()).unwrap();
f_dest.write_all(b"\")];").unwrap();
}
fn main ()
{
let out_dir = env::var("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap();
static_assets(File::create(Path::new(&out_dir).join("static_html.rs")).unwrap(), "STATIC_HTML", &cwd, "static/index.htm");
static_assets(File::create(Path::new(&out_dir).join("static_css.rs")).unwrap(), "STATIC_CSS", &cwd, "static/css/main.css");
static_assets(File::create(Path::new(&out_dir).join("static_fonts.rs")).unwrap(), "STATIC_FONTS", &cwd, "static/fonts/autobahn/autobahn.woff");
static_assets(File::create(Path::new(&out_dir).join("static_images.rs")).unwrap(), "STATIC_IMAGES", &cwd, "static/images/rifle_16x16.png");
static_assets(File::create(Path::new(&out_dir).join("static_js.rs")).unwrap(), "STATIC_JS", &cwd, "static/js/qr.js");
}
extern crate iron;
use iron::prelude::*;
use iron::middleware::*;
use iron::status;
use iron::headers::ContentType;
use iron::mime::Mime;
#[macro_use]
extern crate router;
// The files referenced below are generated by build.rs
include!(concat!(env!("OUT_DIR"), "/static_html.rs"));
include!(concat!(env!("OUT_DIR"), "/static_css.rs"));
include!(concat!(env!("OUT_DIR"), "/static_fonts.rs"));
include!(concat!(env!("OUT_DIR"), "/static_images.rs"));
include!(concat!(env!("OUT_DIR"), "/static_js.rs"));
struct RequestLoggingMiddleware;
impl BeforeMiddleware for RequestLoggingMiddleware
{
fn before (&self, req: &mut Request) -> IronResult<()>
{
println!("{:?}", req);
Ok(())
}
}
fn main ()
{
let router = router!
(
// Static content
index: get "/" => index,
static_html: get "/:file.htm" => static_html,
static_css: get "/css/:file.css" => static_css,
static_eot_fonts: get "/fonts/:file.eot" => static_fonts,
static_woff_fonts: get "/fonts/:file.woff" => static_fonts,
static_ttf_fonts: get "/fonts/:file.ttf" => static_fonts,
static_svg_fonts: get "/fonts/:file.svg" => static_fonts,
static_svg_images: get "/images/:file.svg" => static_images,
static_png_images: get "/images/:file.png" => static_images,
static_jpg_images: get "/images/:file.jpg" => static_images,
static_js: get "/js/:file.js" => static_js,
// Dynamic content
// (No dynamic content yet.)
);
let mut chain = Chain::new(router);
chain.link_before(RequestLoggingMiddleware {});
// https://www.reddit.com/r/rust/comments/61sc6r/can_iron_listen_to_both_ipv4_and_ipv6_at_the_same/
// Iron::new(chain).http("[::]:3000").unwrap();
Iron::new(chain).http("0.0.0.0:3000").unwrap();
}
fn index (req: &mut Request) -> IronResult<Response>
{
static_html(req)
}
fn static_html (req: &mut Request) -> IronResult<Response>
{
static_asset(&STATIC_HTML, ContentType::html().0, req)
}
fn static_css (req: &mut Request) -> IronResult<Response>
{
static_asset(&STATIC_CSS, "text/css".parse().unwrap(), req)
}
fn static_fonts (req: &mut Request) -> IronResult<Response>
{
let mime =
(
match (* req.url.path().last().unwrap()).split('.').last().unwrap()
{
"eot" => Ok("application/vnd.ms-fontobject".parse()),
"woff" => Ok("application/font-woff".parse()),
"ttf" => Ok("application/x-font-truetype".parse()),
"svg" => Ok("image/svg+xml".parse()),
&_ => Err("Unexpected file extension."),
}
).unwrap().unwrap();
static_asset(&STATIC_FONTS, mime, req)
}
fn static_images (req: &mut Request) -> IronResult<Response>
{
let mime =
(
match (* req.url.path().last().unwrap()).split('.').last().unwrap()
{
"svg" => Ok("image/svg+xml".parse()),
"png" => Ok("image/png".parse()),
"jpg" => Ok("image/jpeg".parse()),
&_ => Err("Unexpected file extension."),
}
).unwrap().unwrap();
static_asset(&STATIC_IMAGES, mime, req)
}
fn static_js (req: &mut Request) -> IronResult<Response>
{
// XXX: https://stackoverflow.com/a/4101763
static_asset(&STATIC_JS, "application/javascript".parse().unwrap(), req)
}
fn static_asset (assets_bytes: &[&'static [u8]], mime: Mime, req: &mut Request) -> IronResult<Response>
{
Ok(Response::with((mime, status::Ok, assets_bytes[0])))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment