Explorar o código

Add Rust Rocket framework (#3996)

* add rust rocket framework

* fix gen_range() usage

* fix second argument on gen_range()
Marcelo Barbosa %!s(int64=7) %!d(string=hai) anos
pai
achega
b69e42d542

+ 18 - 0
frameworks/Rust/rocket/Cargo.toml

@@ -0,0 +1,18 @@
+[package]
+name = "rocket"
+version = "0.1.0"
+authors = ["Marcelo Barbosa <[email protected]>"]
+
+[dependencies]
+diesel = { version = "1.3.2", features = ["postgres", "r2d2"] }
+rand = "0.5.5"
+rocket = "0.3.15"
+rocket_codegen = "0.3.15"
+serde = "1.0.71"
+serde_json = "1.0.24"
+serde_derive = "1.0.71"
+
+[dependencies.rocket_contrib]
+version = "*"
+default-features = false
+features = ["json", "handlebars_templates"]

+ 42 - 0
frameworks/Rust/rocket/README.md

@@ -0,0 +1,42 @@
+
+# [Rocket](https://rocket.rs/) - Simple, Fast, Type-Safe Web Framework for Rust
+
+## Description
+
+Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety. All with minimal code.
+
+* [User Guide](https://rocket.rs/guide/)
+* [API Documentation](https://api.rocket.rs/rocket/)
+* Cargo package: [rocket](https://crates.io/crates/rocket)
+
+## Database
+
+PostgreSQL
+
+* ORM using [diesel](http://diesel.rs)
+
+## Test URLs
+
+### Test 1: JSON Encoding 
+
+    http://localhost:8000/json
+
+### Test 2: Single Row Query
+
+    http://localhost:8000/db
+
+### Test 3: Multi Row Query 
+
+    http://localhost:8000/queries?q=20
+
+### Test 4: Fortunes (Template rendering)
+
+    http://localhost:8000/fortunes
+
+### Test 5: Update Query
+
+    http://localhost:8000/updates?q=20
+
+### Test 6: Plaintext
+
+    http://localhost:8000/plaintext

+ 7 - 0
frameworks/Rust/rocket/Rocket.toml

@@ -0,0 +1,7 @@
+[global]
+template_dir = "src/templates/"
+
+[production]
+port = 8000
+log = "critical"
+secret_key = "dY+Rj2ybjGxKetLawKGSWi6EzESKejvENbQ3stffZg0="

+ 30 - 0
frameworks/Rust/rocket/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "rocket",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "query_url": "/queries?q=",
+        "update_url": "/updates?q=",
+        "port": 8000,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "Postgres",
+        "framework": "Rocket",
+        "language": "Rust",
+        "flavor": "None",
+        "orm": "Full",
+        "platform": "Rust",
+        "webserver": "Hyper",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Rocket",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 13 - 0
frameworks/Rust/rocket/rocket.dockerfile

@@ -0,0 +1,13 @@
+FROM rustlang/rust:nightly
+
+ADD ./ /rocket
+
+WORKDIR /rocket
+
+ENV ROCKET_ENV=production
+ENV DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
+
+RUN cargo clean
+RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
+
+CMD ./target/release/rocket

+ 35 - 0
frameworks/Rust/rocket/src/db.rs

@@ -0,0 +1,35 @@
+use std::ops::Deref;
+use rocket::http::Status;
+use rocket::request::{self, FromRequest};
+use rocket::{Request, State, Outcome};
+use diesel::pg::PgConnection;
+use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
+
+type PgPool = Pool<ConnectionManager<PgConnection>>;
+
+pub struct DbConn(pub PooledConnection<ConnectionManager<PgConnection>>);
+
+impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
+    type Error = ();
+
+    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
+        let pool = request.guard::<State<PgPool>>()?;
+        match pool.get() {
+            Ok(conn) => Outcome::Success(DbConn(conn)),
+            Err(_) => Outcome::Failure((Status::ServiceUnavailable, ()))
+        }
+    }
+}
+
+impl Deref for DbConn {
+    type Target = PgConnection;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+pub fn init_pool() -> PgPool {
+    let manager = ConnectionManager::<PgConnection>::new(env!("DATABASE_URL"));
+    Pool::new(manager).expect("db pool")
+}

+ 150 - 0
frameworks/Rust/rocket/src/main.rs

@@ -0,0 +1,150 @@
+#![feature(plugin, custom_derive)]
+#![plugin(rocket_codegen)]
+
+extern crate rand;
+extern crate rocket;
+extern crate rocket_contrib;
+#[macro_use] extern crate diesel;
+#[macro_use] extern crate serde_derive;
+
+use diesel::prelude::*;
+use diesel::result::Error;
+use rand::Rng;
+use rocket_contrib::Json;
+use rocket_contrib::Template;
+
+mod db;
+mod models;
+mod schema;
+
+#[derive(FromForm)]
+struct QueryString {
+    q: u16,
+}
+
+fn random_number() -> i32 {
+    rand::thread_rng().gen_range(1, 10_001)
+}
+
+#[get("/plaintext")]
+fn plaintext() -> &'static str {
+    "Hello, World!"
+}
+
+#[get("/json")]
+fn json() -> Json<models::Message> {
+    let message = models::Message {
+        message: "Hello, World!"
+    };
+    Json(message)
+}
+
+#[get("/db")]
+fn db(conn: db::DbConn) -> Json<models::World> {
+    use schema::world::dsl::*;
+
+    let result = world
+        .filter(id.eq(random_number()))
+        .first::<models::World>(&*conn)
+        .expect("error loading world");
+
+    Json(result)
+}
+
+#[get("/queries")]
+fn queries_empty(conn: db::DbConn) -> Json<Vec<models::World>> {
+    queries(conn, QueryString { q: 1 })
+}
+
+#[get("/queries?<qs>")]
+fn queries(conn: db::DbConn, qs: QueryString) -> Json<Vec<models::World>> {
+    use schema::world::dsl::*;
+
+    let mut q = qs.q;
+    if q == 0 { q = 1 }
+    if q > 500 { q = 500; }
+
+    let mut results = Vec::with_capacity(q as usize);
+
+    for _ in 0..q {
+        let result = world
+            .filter(id.eq(random_number()))
+            .first::<models::World>(&*conn)
+            .expect("error loading world");       
+        results.push(result);
+    }
+
+    Json(results)
+}
+
+#[get("/fortunes")]
+fn fortunes(conn: db::DbConn) -> Template {
+    use schema::fortune::dsl::*;
+
+    let mut context = fortune
+        .load::<models::Fortune>(&*conn)
+        .expect("error loading fortunes");
+    
+    context.push(models::Fortune { 
+        id: 0,
+        message: "Additional fortune added at request time.".to_string()
+    });
+    
+    context.sort_by(|a, b| a.message.cmp(&b.message));
+
+    Template::render("fortunes", &context)
+}
+
+#[get("/updates")]
+fn updates_empty(conn: db::DbConn) -> Json<Vec<models::World>> {
+    updates(conn, QueryString { q: 1 })
+}
+
+#[get("/updates?<qs>")]
+fn updates(conn: db::DbConn, qs: QueryString) -> Json<Vec<models::World>> {
+    use schema::world::dsl::*;
+
+    let mut q = qs.q;
+    if q == 0 { q = 1 }
+    if q > 500 { q = 500; }
+
+    let mut results = Vec::with_capacity(q as usize);
+
+    for _ in 0..q {
+        let mut result = world
+            .filter(id.eq(random_number()))
+            .first::<models::World>(&*conn)
+            .expect("error loading world");
+        result.randomNumber = random_number();
+        results.push(result);
+    }
+
+    let _ = conn.transaction::<(), Error, _>(|| {
+        for w in &results {
+            let _ = diesel::update(world)
+                .filter(id.eq(w.id))
+                .set(randomnumber.eq(w.randomNumber))
+                .execute(&*conn);
+        }
+        Ok(())
+    });
+
+    Json(results)
+}
+
+fn main() {
+    rocket::ignite()
+        .mount("/", routes![
+            json,
+            plaintext,
+            db,
+            queries,
+            queries_empty,
+            fortunes,
+            updates,
+            updates_empty,
+        ])
+        .manage(db::init_pool())
+        .attach(Template::fairing())
+        .launch();
+}

+ 17 - 0
frameworks/Rust/rocket/src/models.rs

@@ -0,0 +1,17 @@
+#[derive(Serialize)]
+pub struct Message {
+    pub message: &'static str,
+}
+
+#[allow(non_snake_case)]
+#[derive(Serialize, Queryable)]
+pub struct World {
+    pub id: i32,
+    pub randomNumber: i32,
+}
+
+#[derive(Serialize, Queryable)]
+pub struct Fortune {
+    pub id: i32,
+    pub message: String,
+}

+ 15 - 0
frameworks/Rust/rocket/src/schema.rs

@@ -0,0 +1,15 @@
+#![allow(non_snake_case)]
+
+table! {
+    world (id) {
+        id -> Integer,
+        randomnumber -> Integer,
+    }
+}
+
+table! {
+    fortune (id) {
+        id -> Integer,
+        message -> Text,
+    }
+}

+ 10 - 0
frameworks/Rust/rocket/src/templates/fortunes.html.hbs

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+{{#each .}}<tr><td>{{id}}</td><td>{{message}}</td></tr>{{/each}}
+</table>
+</body>
+</html>