presentation-rust-webapps/rust_web.org

944 lines
23 KiB
Org Mode
Raw Permalink Normal View History

2021-04-14 17:12:12 +02:00
#+STARTUP: beamer
#+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline
#+OPTIONS: author:t broken-links:nil c:nil creator:nil
#+OPTIONS: d:(not "LOGBOOK") date:t e:t email:nil f:t inline:t num:t
#+OPTIONS: p:nil pri:nil prop:nil stat:t tags:t tasks:t tex:t
#+OPTIONS: timestamp:t title:t toc:t todo:t |:t
2021-04-14 17:12:12 +02:00
#+OPTIONS: ^:nil
2020-12-21 14:09:32 +01:00
#+TITLE: Rust, Rocket and Diesel
2021-04-14 17:12:12 +02:00
#+SUBTITLE: Web apps with Rust
2020-12-21 14:09:32 +01:00
#+DESCRIPTION: An introduction to Rust, Rocket and Diesel.
#+KEYWORDS: rust rocket diesel
2021-04-14 17:12:12 +02:00
#+AUTHOR: \href{mailto:finga@onders.org}{finga}
#+EMAIL: finga@onders.org
#+LANGUAGE: en
#+SELECT_TAGS: export
#+EXCLUDE_TAGS: noexport
#+CREATOR: Emacs 26.1 (Org mode 9.1.9)
#+OPTIONS: H:2
#+LATEX_CLASS: beamer
2021-04-14 17:12:12 +02:00
#+LATEX_CLASS_OPTIONS: [aspectratio=1610]
2020-12-21 11:04:02 +01:00
# #+LATEX_HEADER: \hypersetup{colorlinks=true,linkcolor=gray}
#+LATEX_HEADER: \hypersetup{colorlinks=true,linkcolor=black,urlcolor=gray}
2021-04-14 17:12:12 +02:00
# #+LATEX_HEADER: \usepackage{listings, listings-rust}
#+COLUMNS: %45ITEM %10BEAMER_env(Env) %10BEAMER_act(Act) %4BEAMER_col(Col) %8BEAMER_opt(Opt)
#+BEAMER_THEME: Frankfurt
#+BEAMER_COLOR_THEME: seagull
#+BEAMER_FONT_THEME:
#+BEAMER_INNER_THEME:
#+BEAMER_OUTER_THEME:
#+BEAMER_HEADER: \institute[INST]{\href{https://cccsbg.at}{cccsbg}}
* Rust
** What is Rust?
2020-12-21 11:04:02 +01:00
#+BEGIN_CENTER
#+LaTeX:\includegraphics[width = 0.65\textwidth]{img/Bruine_roest_op_tarwe_(Puccinia_recondita_f.sp._tritici_on_Triticum_aestivum).jpg}
#+END_CENTER
2020-12-21 14:09:32 +01:00
*** Rust, the language
2021-01-11 22:44:15 +01:00
- About 10 years young (2010)
2020-12-21 11:04:02 +01:00
- Memory safe without gcing, optional ref counting
2021-04-14 17:12:12 +02:00
- Ownership, lifetimes, traits, functional paradigms
2020-12-21 11:04:02 +01:00
- Variables are immutable by default and can be shadowed
- Performance of idiomatic Rust is comparable to the performance
of idiomatic cpp
2021-04-14 17:12:12 +02:00
** How to install
*** Install Rust
#+BEGIN_SRC sh
$ curl --proto '=https' --tlsv1.2 -sSf \
https://sh.rustup.rs | sh
#+END_SRC
*** Install nightly Rust (needed for Rocket)
#+BEGIN_SRC sh
$ rustup toolchain install nightly
#+END_SRC
*** Install rustfmt (linter and code formatter)
#+BEGIN_SRC sh
$ rustup component add rustfmt
#+END_SRC
*** Install clippy (static code analysis)
#+BEGIN_SRC sh
$ rustup component add clippy
#+END_SRC
** Where to find more information?
2020-12-21 11:04:02 +01:00
2020-12-21 14:09:32 +01:00
*** Docs...
2021-04-14 17:12:12 +02:00
- [[https://doc.rust-lang.org/book/][The Rust Book]] (~$ rustup docs --book~)
- [[https://doc.rust-lang.org/rust-by-example/][Rust by Example]] (~$ rustup docs --rust-by-example~)
2021-01-11 22:44:15 +01:00
- [[https://doc.rust-lang.org/std/][The Rust Standard Library]] (~$ rustup docs --std~)
- Use '~$ rustup help docs~' to get an overview
2020-12-21 11:04:02 +01:00
- [[https://cheats.rs][cheats.rs]]
2021-01-11 22:44:15 +01:00
- [[https://programming-idioms.org/cheatsheet/Rust][programming-idioms.org]]
2021-04-14 17:12:12 +02:00
- [[https://crates.io][crates.io]]/[[https://lib.rs][lib.rs]]
2020-12-21 11:04:02 +01:00
2020-12-21 14:09:32 +01:00
*** Chat (Discord)
- [[https://discord.gg/rust-lang][The Rust Programming Language]]
- [[https://discord.com/invite/tcbkpyQ][The Rust Programming Language Community Server]]
2021-04-14 17:12:12 +02:00
** Hello, World!
Executing ~`cargo new foobar`~ creates a new project...
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
*** foobar/Cargo.toml
#+BEGIN_SRC toml
[package]
name = "foo"
version = "0.1.0"
authors = ["finga <finga@onders.org>"]
edition = "2018"
#+END_SRC
\tiny
#+BEGIN_SRC toml
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
#+END_SRC
\normalsize
#+BEGIN_SRC toml
[dependencies]
#+END_SRC
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
*** foobar/src/main.rs
#+BEGIN_SRC rust
fn main() {
println!("Hello, world!");
}
#+END_SRC
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
** Mutability and shadowing
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
*** Immutable variable
#+BEGIN_SRC rust
let foo = 5;
2020-12-21 11:04:02 +01:00
#+END_SRC
2021-04-14 17:12:12 +02:00
*** Mutable variable
#+BEGIN_SRC rust
let mut bar = 5;
bar = 6;
2020-12-21 11:04:02 +01:00
#+END_SRC
2021-04-14 17:12:12 +02:00
*** Shadowing
#+BEGIN_SRC rust
let baz = 5;
let baz = baz + 3;
2020-12-21 11:04:02 +01:00
#+END_SRC
2021-04-14 17:12:12 +02:00
** Structs
*** Definition of a struct
#+BEGIN_SRC rust
struct Something {
id: usize,
item: String,
}
#+END_SRC
*** Instantiation of a struct
#+BEGIN_SRC rust
let something = Something {
id: 0,
item: String::from("an item"),
};
#+END_SRC
*** Access of a struct's field
#+BEGIN_SRC rust
something.item
#+END_SRC
** Tuple structs
*** Definition of a tuple struct
#+BEGIN_SRC rust
struct Box(usize, usize, usize);
#+END_SRC
*** Instantiation of a tuple struct
#+BEGIN_SRC rust
let first_box = Box(3, 5, 7);
#+END_SRC
*** Access of a tuple struct's field
#+BEGIN_SRC rust
first_box.0;
first_box.1;
first_box.2;
#+END_SRC
** Error related types
- Heavy use of ~Option<T>~ and ~Result<T, E>~
*** Option
#+BEGIN_SRC rust
pub enum Option<T> {
None,
Some(T),
}
#+END_SRC
*** Result
#+BEGIN_SRC rust
pub enum Result<T, E> {
Ok(T),
Err(E),
}
#+END_SRC
** Error handling
*** Unrecoverable errors \rightarrow panics
#+BEGIN_SRC rust
fn get_data() -> Data {
...
panic!("Cannot get data");
}
2020-12-21 11:04:02 +01:00
#+END_SRC
2021-04-14 17:12:12 +02:00
*** Recoverable error with anyhow
#+BEGIN_SRC rust
fn get_data() -> Result<Data> {
...
Ok(data) // return is only used for "early" returns
}
...
let data = get_data()?;
...
#+END_SRC
** ~match~ statements
- Must be exhaustive
- All match arms must return the same type!
- Can be used everywhere e.g. ~let foo = match ...~
- @@latex:{\color{red}@@All match arms must return the same type!!1!@@latex:}@@
*** Recoverable error with anyhow
#+BEGIN_SRC rust
match age {
0 => unreachable!("Too young to be a person"),
n @ 1..=12 => println!("Child of age {}", n),
n @ 13..=19 => println!("Teen of age {}", n),
n @ 20..=150 => println!("Person of age {}", n),
_ => unimplemented!("Nobody I know got that old"),
}
#+END_SRC
** ~if let~ statements
*** Sometimes an ~if let~ is nicer than a ~match~ or an ~if/else~ statement
#+BEGIN_SRC rust
if let Some(actual_age) = age {
println!("The age is {}", actual_age);
}
#+END_SRC
** Use functional language features
- ~vec![1, 2, 3].iter()~
- ~vec![1, 2, 3].iter().sum()~
- ~vec![1, 2, 3].iter().map(|x| x + 1).collect()~
- ~foo.into_iter().filter(|i| i.bar == 0).collect()~
- ~Iterator~ trait
** Tests
- Use ~assert!()~ family
- Unit tests
- Put them into a test module
- Annotate the test module with ~#[cfg(test)]~
- Annotate the tests with ~#[test]~
- Documentation testing
- Integration testing
- Integration tests are put into the ~src/tests/~ directory
** Docs
- Documentation comments: ~///~
- Markdown notation
- ~cargo doc~ runns ~rustdoc~ which generates html docs
- ~cargo doc --open~ runs ~rustdoc~ and opens the result in a browser
- ~rustdoc~ documentation: https://doc.rust-lang.org/beta/rustdoc/index.html
2020-12-21 11:04:02 +01:00
* Rocket
** What is Rocket?
2020-12-21 14:09:32 +01:00
#+BEGIN_QUOTE
Rocket is a web framework for Rust that makes it simple to write
fast, secure web applications without sacrificing flexibility,
usability, or type safety.
#+END_QUOTE
*** Features
**** Col left
:PROPERTIES:
:BEAMER_col: 0.45
:END:
- Type Safe
- Extensible
- Templating
2021-04-14 17:12:12 +02:00
- (Secure) Cookies
2020-12-21 14:09:32 +01:00
**** Col right
:PROPERTIES:
:BEAMER_col: 0.45
:END:
- Streams
- Testing (Unit tests)
- Typed URIs
2021-04-14 17:12:12 +02:00
- Single binary
** Create a new Rocket project
*** Create the crate
#+BEGIN_SRC sh
cargo init new_project
cd new_project
rustup override set nightly
cargo r
#+END_SRC
*** Add Rocket dependencies to ~Cargo.toml~
#+BEGIN_SRC toml
[dependencies]
rocket = "0.4"
#+END_SRC
** Hello Rocket
*** ~src/main.rs~
#+BEGIN_SRC rust
#![feature(decl_macro)]
use rocket::{get, routes};
#[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();
}
#+END_SRC
** Receive data
*** Struct ~EmailForm~
#+BEGIN_SRC rust
#[derive(Debug, FromForm)]
struct EmailForm {
email: String,
}
#+END_SRC
*** Procedure using that form data
#+BEGIN_SRC rust
#[post("/reset", data = "<email>")]
fn reset_email(email: Form<EmailForm>) -> Flash<Redirect> {
...
Flash::success(
Redirect::to(uri!(reset)),
format!("Email was sent to {}", email.email),
)
}
#+END_SRC
** Flash messages
#+BEGIN_SRC rust
#[get("/reset")]
fn reset(flash: Option<FlashMessage>) -> String {
if let Some(ref msg) = flash { // `let ref x = y` is
msg.msg() // equivalent to `let x = &y`
} else {
format!("Hello, reset!");
}
}
#+END_SRC
** Templates
- Use the ~rocket_contrib~ crate!
*** ~templates/name.html.tera~
:PROPERTIES:
:BEAMER_col: 0.35
:BEAMER_env: block
:END:
#+BEGIN_SRC html
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<p> Hello,
{{ name }}!</p>
</body>
</html>
#+END_SRC
*** ~src/main.rs~
:PROPERTIES:
:BEAMER_col: 0.60
:BEAMER_env: block
:END:
#+BEGIN_SRC rust
#[get("/hello/<name>")]
fn hello(name: String) -> Template {
let mut context = HashMap::new();
context.insert("name", name);
Template::render("name", &context)
}
...
rocket::ignite()
.mount(...)
.attach(Template::fairing())
.launch();
#+END_SRC
2020-12-21 14:09:32 +01:00
2020-12-21 15:11:43 +01:00
** Where to get further information?
2020-12-21 14:09:32 +01:00
*** Docs...
2020-12-21 15:11:43 +01:00
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
- [[https://rocket.rs][rocket.rs]]
2020-12-21 14:09:32 +01:00
- [[https://rocket.rs/v0.4/guide/][Rocket Guide]]
- [[https://api.rocket.rs/v0.4/rocket/][Rocket API docs]]
- [[https://github.com/SergioBenitez/Rocket/tree/v0.4/examples][Examples]]
*** Chat
2020-12-21 15:11:43 +01:00
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
2020-12-21 14:09:32 +01:00
- Matrix: [[https://chat.mozilla.org/#/room/%23rocket:mozilla.org][#rocket:mozilla.org]]
- IRC on Freenode: [[https://kiwiirc.com/client/chat.freenode.net/#rocket][#rocket]]
2020-12-21 11:04:02 +01:00
* Diesel
2021-04-14 17:12:12 +02:00
** ORM
2020-12-21 15:11:43 +01:00
Object-relational-mapping is a programming technique for converting
data between incompatible type systems using the paradigms of your
programming language.
*** Pros
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
- Models are DRY (don't repeat yourself)
- Cleaner separation
- Prepared and sanitised queries
- Changes are versioned
*** Cons
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
- Debugging can get more complicated (performance)
- No need to learn SQL
** Prerequisites ~diesel_cli~
*** Setup Diesel for default DBMS
#+BEGIN_SRC sh
$ sudo apt install libpq-dev libsqlite3-dev \
default-libmysqlclient-dev
$ cargo install diesel_cli
#+END_SRC
*** Setup Diesel for SQLite only
#+BEGIN_SRC sh
$ sudo apt install libsqlite3-dev
$ cargo install diesel_cli -no-default-features \
--features sqlite
#+END_SRC
** How does that look in Rust
*** Define a struct
:PROPERTIES:
:BEAMER_col: 0.50
:BEAMER_env: block
:END:
#+BEGIN_SRC rust
#[derive(Queryable)]
pub struct NewComment<'a> {
name: &'a str,
email: &'a str,
comment: &'a str,
}
#+END_SRC
*** Insert something
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
#+BEGIN_SRC rust
let user = NewUser {
name: "foo",
email: "foo@bar.baz",
comment: "A comment",
};
2021-04-14 17:12:12 +02:00
...
2020-12-21 15:11:43 +01:00
insert_into(comments)
.values(&user)
.execute(conn);
#+END_SRC
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
** About Diesel
- Enable connection pooling via the ~r2d2~ crate
- Nested joins are not yet nicely implemented
- Database specific SQL regarding each DBMS
- Take a look at the [[https://github.com/diesel-rs/diesel/tree/master/diesel_cli][diesel_cli]] crate for multi DBMS support
- Use ~diesel_cli~ macros and functions for compiling migration
functionality into the application
2020-12-21 15:11:43 +01:00
** Where to get further information?
*** Docs...
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
- [[https://diesel.rs][diesel.rs]]
- [[https://diesel.rs/guides/][Diesel Guides]]
- [[https://docs.diesel.rs/master/diesel/index.html][Diesel API docs]]
- [[https://github.com/diesel-rs/diesel/tree/master/examples][Examples]] (minimalistic)
*** Support
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
- [[https://gitter.im/diesel-rs/diesel][Gitter]]
- [[https://discourse.diesel.rs/][Discourse Forum]]
2020-12-21 11:04:02 +01:00
* Lets code something
2020-12-21 15:11:43 +01:00
** Lets create a small web app
2021-04-14 17:12:12 +02:00
A chat box where everybody can post.
2021-01-11 22:44:15 +01:00
#+BEGIN_CENTER
#+LaTeX:\includegraphics[width = 0.7\textwidth]{img/challenge-accepted-lets-code-it.jpg}
#+END_CENTER
2020-12-21 15:11:43 +01:00
*** Steps to reproduce :noexport:
**** Create new crate
2021-01-12 17:55:25 +01:00
#+BEGIN_SRC sh
cargo init simple_text_board
cd simple_text_board
rustup override set nightly
cargo r
#+END_SRC
2021-01-11 23:10:26 +01:00
**** Setup Rocket
2021-01-12 17:55:25 +01:00
***** Add Rocket to ~Cargo.toml~
#+BEGIN_SRC toml
[dependencies]
rocket = "0.4"
#+END_SRC
***** Add ~Rocket.toml~
#+BEGIN_SRC toml
[development]
address = "0.0.0.0"
port = 8000
workers = 2
keep_alive = 5
log = "normal"
#+END_SRC
***** Modify ~src/main.rs~ for Rockets "~Hello, world!~" program
#+BEGIN_SRC rust
2021-04-14 17:12:12 +02:00
// #![feature(proc_macro_hygiene, decl_macro)]
#![feature(decl_macro)]
2021-01-12 17:55:25 +01:00
2021-04-14 17:12:12 +02:00
// #[macro_use] extern crate rocket;
// #[macro_use] extern crate diesel;
2021-01-12 17:55:25 +01:00
2021-04-14 17:12:12 +02:00
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
2021-01-12 17:55:25 +01:00
2021-04-14 17:12:12 +02:00
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}
2021-01-12 17:55:25 +01:00
#+END_SRC
**** Create first templates
***** Create the base template at ~templates/base.html.tera~
#+BEGIN_SRC html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>simple text board</title>
</head>
<body>
<ul>
<li><a href="/">home</a></li>
<li><a href="/create">create post</a></li>
</ul>
{% block content %} {% endblock %}
</body>
</html>
#+END_SRC
***** Create the create template at ~templates/create.html.tera~
#+BEGIN_SRC html
{% extends "base" %}
{% block content %}
<h1>simple text board</h1>
<h2>create a post</h2>
<form action="/create" method="post" accept-charset="utf-8">
<label for="author">author name</label>
<input type="text" placeholder="author" name="author" required>
<label for="email">email</label>
<input type="text" placeholder="email" name="email">
<label for="title">title</label>
<input type="text" placeholder="title" name="title" required>
<label for="content">content</label>
<textarea type="text" placeholder="content" name="content" rows="8" cols="50" required>
</textarea>
<button type="submit">create</button>
{% if flash %}<p>{{ flash }}</p>{% endif %}
</form>
{% endblock content %}
#+END_SRC
***** Create the ~create_form~ function and add it to routing
#+BEGIN_SRC rust
#![feature(proc_macro_hygiene, decl_macro)]
use std::collections::HashMap;
use rocket::{get, request::FlashMessage, routes};
use rocket_contrib::templates::Template;
#[get("/create")]
fn create_form(flash: Option<FlashMessage>) -> Template {
let mut context = HashMap::new();
if let Some(ref msg) = flash {
context.insert("flash", msg.msg());
}
Template::render("create", &context)
}
...
fn main() {
rocket::ignite()
.mount("/", routes![index, create_form])
.attach(Template::fairing())
.launch();
}
#+END_SRC
**** Setup Database and posts
***** Create the ~.env~ file
#+BEGIN_SRC sh
DATABASE_URL=simple_text_board.sqlite
#+END_SRC
***** Add diesel to the dependencies and to ~rocket_contrib~ in ~Cargo.toml~
#+BEGIN_SRC toml
...
chrono = "0.4"
diesel = { version = "1.4", features = ["sqlite", "chrono"] }
log = "0.4"
...
[dependencies.rocket_contrib]
...
features = ["diesel_sqlite_pool", "tera_templates"]
#+END_SRC
***** Configure database for Rocket in ~Rocket.toml~
#+BEGIN_SRC toml
[global.databases]
sqlite = { url = "simple_text_board.sqlite" }
#+END_SRC
***** Create the first table schema
#+BEGIN_SRC sh
diesel setup
diesel migration generate create_posts
#+END_SRC
***** How to create the table
#+BEGIN_SRC sql
CREATE TABLE posts (
id INTEGER NOT NULL PRIMARY KEY,
parent INTEGER,
timestamp DATETIME NOT NULL,
author VARCHAR NOT NULL,
email VARCHAR NOT NULL,
title VARCHAR NOT NULL,
content VARCHAR NOT NULL,
FOREIGN KEY (parent) REFERENCES posts(id)
);
#+END_SRC
***** How to destroy it
#+BEGIN_SRC sql
DROP TABLE posts;
#+END_SRC
***** Applay and test rollback
#+BEGIN_SRC sh
diesel migration run
diesel migration redo
#+END_SRC
***** Create data model in ~src/models.rs~
#+BEGIN_SRC rust
use crate::schema::posts::{self, dsl::posts as table_posts};
use chrono::NaiveDateTime;
use diesel::{QueryResult, RunQueryDsl};
#[derive(Insertable)]
#[table_name = "posts"]
pub struct NewPost<'a> {
pub parent: Option<&'a i32>,
pub timestamp: NaiveDateTime,
pub author: &'a str,
pub email: &'a str,
pub title: &'a str,
pub content: &'a str,
}
impl NewPost<'_> {
pub fn insert(&self, conn: &diesel::SqliteConnection) -> QueryResult<usize> {
diesel::insert_into(table_posts).values(self).execute(conn)
}
}
#+END_SRC
***** Add create post functionality to ~src/main.rs~
#+BEGIN_SRC rust
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate diesel;
use std::{collections::HashMap, net::SocketAddr};
use chrono::Local;
use diesel::SqliteConnection;
use log::{error, info};
use rocket::{
get, post,
request::{FlashMessage, Form},
response::{Flash, Redirect},
routes, uri, FromForm,
};
use rocket_contrib::{database, templates::Template};
mod models;
mod schema;
use models::NewPost;
#[database("sqlite")]
struct DbCon(SqliteConnection);
#[derive(FromForm)]
pub struct PostForm {
author: String,
email: String,
title: String,
content: String,
}
...
#[post("/create", data = "<post>")]
fn create_post(
conn: DbCon,
remote_address: SocketAddr,
post: Form<PostForm>,
) -> Result<Redirect, Flash<Redirect>> {
let post = NewPost {
parent: None,
timestamp: Local::now().naive_local(),
author: &post.author.trim(),
email: &post.email.trim(),
title: &post.title.trim(),
content: &post.content.trim(),
};
match post.insert(&conn) {
Ok(_) => {
info!("New post from {}", remote_address);
Ok(Redirect::to(uri!(index)))
}
Err(e) => {
error!("Could not create post from {}: {:?}", remote_address, e);
Err(Flash::error(
Redirect::to(uri!(create_form)),
"Could not create post.".to_string(),
))
}
}
}
...
fn main() {
rocket::ignite()
.mount("/", routes![index, create_form, create_post])
.attach(DbCon::fairing())
.attach(Template::fairing())
.launch();
}
#+END_SRC
2021-01-11 23:10:26 +01:00
2021-04-14 17:07:10 +02:00
**** Create a template to list all posts
***** Add a template for the index page ~templates/index.html.tera~
#+BEGIN_SRC html
{% extends "base" %}
{% block content %}
<h1>simple text board</h1>
<h2>index</h2>
<ul>
{% for post in posts %}
<li>{{ loop.index }} - <a href="post/{{ post.id }}">{{ post.title }}</a> - {{ post.author }}</li>
{% endfor %}
</ul>
{% endblock content %}
#+END_SRC
***** Add serde dependencies to ~Cargo.toml~
#+BEGIN_SRC toml
chrono = { version = "0.4", features = ["serde"] }
...
serde = { version = "1.0", features = ["derive"] }
#+END_SRC
***** Add `Post` to ~src/models.rs~
#+BEGIN_SRC rust
...
use diesel::{QueryResult, RunQueryDsl, SqliteConnection};
use serde::Serialize;
...
#[derive(Debug, Queryable, Serialize)]
pub struct Post {
pub id: i32,
pub parent: Option<i32>,
pub timestamp: NaiveDateTime,
pub author: String,
pub email: String,
pub title: String,
pub content: String,
}
impl Post {
pub fn get_all(connection: &SqliteConnection) -> QueryResult<Vec<Post>> {
posts::table.load::<Post>(connection)
}
}
#+END_SRC
***** Add index template in ~src/main.rs~
#+BEGIN_SRC rust
...
use serde::Serialize;
...
use models::{NewPost, Post};
...
#[derive(Serialize)]
struct Posts {
posts: Vec<Post>,
}
#[get("/")]
fn index(conn: DbCon) -> Template {
Template::render(
"index",
Posts {
posts: Post::get_all(&conn).unwrap(),
},
)
}
#+END_SRC
2021-04-14 17:12:12 +02:00
**** Create page for posts
***** Create template ~templates/post.html.tera~
**** Create possibility to reply
2020-12-21 11:04:02 +01:00
* Conclusion
2021-04-14 17:12:12 +02:00
** In general
- Rust's community and ecosystem is growing steadily
- Rust covers a broad field from low level code to high level
- Rocket and libraries alike improve creating security and
performance focused web application development
- Diesel is on a good way to become a serious ORM
- Alltogether Rust is ready to be used for web apps
2020-12-21 11:04:02 +01:00
** What did we do?
2021-01-11 22:44:15 +01:00
- Setup Rust and Diesel
- Create a project with Rocket and Diesel prerequisites
2021-04-14 17:12:12 +02:00
- Store a small model with Diesel in a SQLite DB
- Create and use Tera templates
2020-12-21 11:04:02 +01:00
2021-04-14 17:12:12 +02:00
** Where to continue
- Move DB setup into the project
- Add features (List posts by Author, paging, etc..)
- (Private) Cookies
2020-12-21 11:04:02 +01:00
2020-12-21 14:09:32 +01:00
** Two small projects
*** [[https://git.onders.org/finga/ldap0r][ldap0r]]
2020-12-21 15:11:43 +01:00
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
2020-12-21 14:09:32 +01:00
- LDAP password reset tool
2021-04-14 17:12:12 +02:00
- ($\sim$ 300 SloC without tests)
2020-12-21 14:09:32 +01:00
- First release
- Tests
2021-04-14 17:12:12 +02:00
*** [[https://git.onders.org/finga/webhookey][webhookey]]
2020-12-21 15:11:43 +01:00
:PROPERTIES:
:BEAMER_col: 0.45
:BEAMER_env: block
:END:
2021-04-14 17:12:12 +02:00
- Execute scripts triggered by http(s) requests
- Is becoming more and more robust
- ($\sim$ 400 SloC without tests)
2020-12-21 11:04:02 +01:00
** The End
#+BEGIN_CENTER
#+LaTeX:\includegraphics[width = 0.3\textwidth]{img/goodbye.jpg}
\Huge
Thanks for your attention!!1!
This is the end..
#+END_CENTER