class: center, middle

# Full stack Rust

### Hyper Diesel Rust Rocket

<div style="font-size: 16px">Johannes Schriewer aka. Dunkelstern</div>

---
class: center, middle

<img src="/images/xwing.png" width="100%">

Wenn es fertig ist sieht das ganze so aus...

???

- Ein bisschen rostig für den Charme
- Flink
- Mit kleinem sidekick
- Mit ein bisschen glück kann man auch den Todesstern vernichten.

---
class: center, middle

Wenn man genau hinschaut ist sogar R2D2 mit an Board:

<img src="/images/r2d2.png" width="100%">

???

Und tausend anderen Modulen, hello javascript

---
class: center, middle

In Go sähe es dann so aus:

<img src="/images/tie-fighter.png" width="60%">

???

Ok ein bisschen Spaß muss sein...

---

# Übersicht

1. Wer bin ich
2. Rust im Backend
3. Rust für Datenbanken
4. Rust im Browser (WASM)

---

# Wer bin ich

- Backend-Entwickler bei anfema
- Normalerweise schreibe ich Python und Node.js
- Code seit 2003 in diversen Bereichen
- Enttäuscht von Swift

--
- Manche sagen ich bin ein irrer Bastler

--
- Wenn ich nicht Code bastel ich Elektronikgerümpel

---

# Rust im Backend

- Es gibt verschiedene Frameworks

--

    - Iron
    - Nickel
    - Conduit
    - Rocket
    - Gotham
    - ...

???
- Iron: Juni 2014, Aktuell kein Maintainer
- Nickel: Juni 2014, API ähnlich Express
- Conduit: Juni 2014, Wahrscheinlich tot
- Rocket: März 2016, Aktive Entwicklung
- Gotham: März 2017, Viele Beispiele, Aktive Entwicklung

--
- Das Benutzerfreundlichste ist [Rocket](https://rocket.rs)
--
 (IMHO)



---

## Beispiel: Rocket

```rust
#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/hello/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

fn main() {
    rocket::ignite().mount("/", routes![hello]).launch();
}
```

???

- Wir brauchen Compiler Plugins
- ... und den Rocket Code-Generator für weniger Boilerplate
- Dann bauen wir uns einen Endpunkt
- Python Flask
- Return String, `format` ist ein macro
- Start mit einer einzelnen route

---

## JSON API Beispiel

### Model

```rust
#![feature(custom_derive)]

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;

use chrono::prelude::*;

#[derive(Serialize, Deserialize, Debug)]
pub struct Person {
    pub id: i32,
    pub name: String,
    pub birthday: NaiveDateTime,
}
```

???

- Wieder ein neues rust-Feature
- Serde: Serializer-Deserializer
- Chrono: Date/Time lib
- Boilerplate lassen wir wieder generieren
- `Person` ist unser Model

---

### View

```rust
use rocket::response::Failure;
use rocket_contrib::Json;

#[get("/person/<id>", format = "application/json")]
fn get_person(id: i32) -> Result<Json<Person>, Failure> {
    Ok(Json(Person {
        id,
        name: String::from("Max Musterman"),
        birthday: Local::now().naive_local(),
    }))
}
```

Die anderen HTTP Methoden funktionieren natürlich auch...

???

- JSON-Support laden
- Mock-Endpoint
- Statische Daten als Beispiel
- Reagiert nur auf `Accept: application/json`

---

#### Ergebnis

<div style="margin-top: -20px; white-space: pre-wrap; font-family: Ubuntu Mono; font-size: 20px;">
<span style="color: #808000;">$ curl -v http://localhost:8080/person/1 \
      -H 'Accept: application/json'</span>
<span style="color: #008000;">&gt; GET /person/1 HTTP/1.1
&gt; Host: localhost:8080
&gt; User-Agent: curl/7.59.0
&gt; Accept: application/json
&gt;</span>
<span style="color: #800000;">&lt; HTTP/1.1 200 OK
&lt; Content-Type: application/json
&lt; Server: Rocket
&lt; Content-Length: 74
&lt; Date: Sun, 15 Apr 2018 22:32:27 GMT
&lt;</span>
<span style="color: #0000c0;">{
    "id": 1,
    "name": "Max Musterman",
    "birthday":"2018-04-16T00:32:27.480509862"
}</span>
</div>

???

- `Accept`-Header nicht vergessen sonst 404
- JSON formatiert zur besseren lesbarkeit

---

### Request Guards (aka Middleware)

```rust
#[get("/admin")]
fn admin_panel(admin: AdminUser) -> &'static str {
    "Hello, administrator. This is the admin panel!"
}

#[get("/admin", rank = 2)]
fn admin_panel_user(user: User) -> &'static str {
    "You must be an administrator to access this page."
}

#[get("/admin", rank = 3)]
fn admin_panel_redirect() -> Redirect {
    Redirect::to("/login")
}
```

Rocket wählt automatisch denjenigen View der alle Request guards unterstützt (und den niedrigsten Rank hat)

???

- Middleware als opt-in
- Die Parameter-Typen implementieren `FromRequest`-Trait
- Nach Rank
- Request Guards müssen erfüllt werden sonst 404
- `format` im Decorator funktioniert ähnlich

---

# Rust für Datenbanken

- Auch hier wieder verschiedene Dinge:

--
    - Postgres-Lib direkt
    - Rustorm
    - Diesel
    - ...
???

- Postgres-Lib: Nicht direkt typesafe, bzw dynamisch
    - Langsam
- Rustorm: Rewrite in progress, schlechte Doku
- Diesel: Dürftige Doku aber es geht voran
    - Schneller als postgres-lib direkt
--
- Ich habe mich auf [Diesel](https://diesel.rs) eingeschossen weil es typesafe ist.


---

## Model definition (Diesel)

1. Table
```rust
table! {
        person (id) {
            id -> Integer,
            name -> Text,
            birthday -> Timestamp,
        }
}
```
2. Model
```rust
#[derive(Serialize, Deserialize, Debug, Queryable, \
             Insertable, Identifiable, AsChangeset)]
#[table_name = "person"]
pub struct Person {
...
```

???

- Migrations: SQL direkt
- `table!` definition lässt sich mit `diesel-cli` automatisieren
- Model wie vorher, mehr `derive`
- Boilerplate wird wieder generiert

---

## View (Diesel)

```rust
#[get("/persons")]
pub fn get_person_list(conn: DbConn)
     -> QueryResult<Json<Vec<Person>>>
{
    person::table
        .order(person::id.asc())
        .load::<Person>(&*conn)
        .map(|person| Json(person))
}

#[get("/person/<id>")]
pub fn get_person(id: i32, conn: DbConn)
    -> Result<Json<Person>, Failure>
{
    person::table
        .find(id)
        .first::<Person>(&*conn)
        .map_err(|_| Failure(Status::NotFound))
        .map(|person| Json(person))
}
```

???

- Sehr vereinfacht
- Kein DB-Connection Error-Handling
- `DbConn` kommt als Request-Guard
- Kaputte DB -> 404
- `map` konvertiert nur _Success_
- `map_err` konvertiert nur _Error_

---

## View (continued)

```rust
#[post("/person", data="<data>")]
pub fn create_person(data: Json<Person>, conn: DbConn)
    -> Result<Json<Person>, Failure>
{
    let rows_inserted = insert_into(person::table)
        .values(&data.into_inner())
        .execute(&*conn)
        .unwrap();

    if rows_inserted != 1 {
        Err(Failure(Status::InternalServerError))
    } else {
        person::table
            .order(person::id.desc())
            .first::<Person>(&*conn)
            .unwrap()
            .map(|person| Json(person))
    }
}
```

???

- Kurzes beispiel für nen DB-Save
- Der `DbConn`-Request-Guard erzeugt und committed transaction
- Nur deshalb ist der Query-Code safe
- Insert and Return geht noch nicht
- Serde und Diesel übernehmen das Decoding der `Person`
- Kein error handling beim insert: `unwrap()` crashed den worker -> 500

---

# Rust im Browser

1. _wasm-bindgen_ und Webpack, JS module in Rust
2. _yew_, React-style Framework, Everything Rust

???

- Beides nur kurz angerissen
- Alles sehr alpha
- Aber es funktioniert und wird weiterentwickelt

---

## WebAssembly Module für Javascript

### Rust Teil

```rust
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}
```

???

- Wir brauchen `bindgen` und ne Menge **nightly** Kram
- Bindgen baut den Typ-Konverter von JS -> WASM
- Die Bindgen Macros bauen Typ-Konverter WASM -> Rust


- Externe Funktion `alert`, kann aber jede beliebige JS funktion sein
- Export von unserer `greet` funktion für JS

---

### Vorbereiten

```bash
$ rustup target add wasm32-unknown-unknown --toolchain nightly
$ cargo install wasm-bindgen-cli
```

### Compile

```bash
$ cargo +nightly build --target wasm32-unknown-unknown
$ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm \
  --out-dir .
```

### Javascript

```js
const rust = import("./wasm_greet");
rust.then(m => m.greet("World!"));
```

???

- Erstmal brauchen wir tooling


- Dann compilen wir den Rust-Code
- Dann bauen wir den JS Glue-Code
- In Production sollte das dann Webpack machen


- Am Ende ist es einfach ein JS module

---

## Direkt in Rust rendern: Yew

https://github.com/DenisKolodin/yew

- JSX Style templates mittels _html!_ macro direkt im Rust code
- Application state management mittels Message passing
- ReactJS und elm waren die inspiration
- Eigener Virtual DOM
- Components und Fragments wie bereits bekannt

???
- Nur angerissen was theoretisch geht
- Projekt existiert seit Dezember 2017
- TODO-MVC geht aber schon

--
- **Very Alpha**

???

- Das ist als Warnung zu verstehen, Ich kenne ja die Javascript leute...

---

Dank [crates.io](https://crates.io) findet man aber noch viele Zusatzmodule

--
<img src="/images/falcon.png" width="100%">

???

So wird dann aus unserem kleinen X-Wing ein großer Millenium Falcon.

Aber trotzdem:

> This is the ship that made the Kessel Run in fourteen parsecs?

---

Es gibt übrigens noch kein Crate namens _Solo_.

C3PO ist leider an den verkehrten Anwendungsfall gebunden, es wäre so schön gewesen wenn das ein I18N Crate wäre.

---

class: center, middle

# Danke für's Zuhören

Kommt gerne auf mich zu wenn ihr mehr wissen wollt!

[Are we web yet? http://www.arewewebyet.org/](http://www.arewewebyet.org/)

---

# Quellen

- [Rocket documentation / Getting started guide](https://rocket.rs/guide/)
- [Diesel documentation](http://diesel.rs/guides/getting-started/)
- [Mozilla Hacks Blog](https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/)
- [Wookiepedia](http://starwars.wikia.com/wiki/Main_Page)

# Kontakt

- hallo@dunkelstern.de
- Twitter: @dunkelstern
- Blog: blog.dunkelstern.de
