Introducing Pencil: A Microframework Inspired By Flask For Rust

Published 08 Mar 2016 under
Posted on Shipeng Feng's blog by Shipeng Feng (_fengsp) :

A Minimal Application

extern crate pencil;

use pencil::{Pencil, Request, Response, PencilResult};

fn hello(_: &mut Request) -> PencilResult {
    Ok(Response::from("Hello World!"))
}

fn main() {
    let mut app = Pencil::new("/web/hello");
    app.get("/", "hello", hello);
    app.run("127.0.0.1:5000");
}

Routing

fn user(r: &mut Request) -> PencilResult {
    let user_id = r.view_args.get("user_id").unwrap();
    Ok(format!("user {}", user_id).into())
}

fn main() {
    // app here
    app.get("/user/<user_id:int>", "user", user);
}

JSON Handling

use std::collections::BTreeMap;
use pencil::jsonify;

fn app_info(_: &mut Request) -> PencilResult {
    let mut d = BTreeMap::new();
    d.insert("name", "hello");
    d.insert("version", "0.1.0");
    return jsonify(&d);
}

fn main() {
    // app here
    app.get("/info", "app_info", app_info);
}

Error Handling

use pencil::HTTPError;

fn page_not_found(_: HTTPError) -> PencilResult {
    let mut response = Response::from("Customized 404 :)");
    response.status_code = 404;
    Ok(response)
}

fn main() {
    // app here
    app.httperrorhandler(404, page_not_found);
}

Static Files

Just create the static folder under your application root path, it just works. Now you can visit http://localhost:5000/static/example.png and get your file. You can customize the static folder and static url path.

fn main() {
    // app here
    app.enable_static_file_handling();
}

Templating

Just create the templates folder under your application root path, it just works. You can customize the template folder.

<!DOCTYPE html>
<html>
  <body>
    <h1>
      Hello !
    </h1>
  </body>
</html>
fn hello_template(request: &mut Request) -> PencilResult {
    let mut context = BTreeMap::new();
    context.insert("name".to_string(), "template".to_string());
    return request.app.render_template("hello.html", &context);
}

fn main() {
    // app here
    app.register_template("hello.html");
    app.get("/hello_template", "hello_template", hello_template);
}

Logging

#[macro_use] extern crate log;
extern crate env_logger;

fn main() {
    // app here
    app.set_debug(true);
    app.set_log_level();
    env_logger::init().unwrap();
    debug!("* Running on http://localhost:5000/");
    app.run("127.0.0.1:5000");
}

Redirects And Errors

use pencil::{redirect, abort};

fn github(_: &mut Request) -> PencilResult {
    return redirect("https://github.com/", 302);
}

fn login(_: &mut Request) -> PencilResult {
    return abort(401);
}

fn main() {
    // app here
    app.get("/github", "github", github);
    app.get("/login", "login", login);
}

Request And Response Objects

They are really easy to use, check docs for more details.

fn search(request: &mut Request) -> PencilResult {
    let keyword = match request.args().get("q") {
        Some(q) => q as &str,
        None => "",
    };
    Ok(Response::from(format!("You are searching for {}", keyword)))
}

fn main() {
    // app here
    app.get("/search", "search", search);
}

Before/After Request

extern crate typemap;

use typemap::Key;

struct KeyType;
struct Value(i32);
impl Key for KeyType { type Value = Value; }

fn before_request(request: &mut Request) -> Option<PencilResult> {
    request.extensions_data.insert::<KeyType>(Value(100));
    None
}

fn main() {
    // app here
    app.before_request(before_request);
}

Modular Applications

use pencil::method::Get;
use pencil::Module;

fn hi_module(_: &mut Request) -> PencilResult {
    Ok("Hi module.".into())
}

fn main() {
    // app here
    let mut demo_module = Module::new("demo", "/web/hello/demo");
    demo_module.route("/demo/hi", &[Get], "hi", hi_module);
    app.register_module(demo_module);
}

Have fun!