Skip to content

Commit 5c43ab9

Browse files
author
okamoto
committed
サンプルwebアプリの作成
1 parent abaea60 commit 5c43ab9

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Cargo.lock
88

99
# These are backup files generated by rustfmt
1010
**/*.rs.bk
11+
12+
**/*.db

part2/todo/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "todo"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
[dependencies]
7+
actix-rt = "1.1.1"
8+
actix-web = "3.3.2"
9+
askama = "0.10.5"
10+
thiserror = "1.0.29"
11+
rusqlite = { version = "0.26", features=["bundled"]}
12+
r2d2 = "0.8.9"
13+
r2d2_sqlite = "0.19.0"
14+
serde = { version = "1.0", features = ["derive"]}

part2/todo/src/main.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use actix_web::{http::header, post, get, web, App, HttpResponse, HttpServer, ResponseError};
2+
use askama::Template;
3+
use thiserror::Error;
4+
use r2d2_sqlite::SqliteConnectionManager;
5+
use r2d2::Pool;
6+
use rusqlite::params;
7+
use serde::Deserialize;
8+
9+
#[derive(Deserialize)]
10+
struct AddParams {
11+
text: String,
12+
}
13+
14+
#[derive(Deserialize)]
15+
struct DeleteParams {
16+
id: u32,
17+
}
18+
19+
#[derive(Debug)]
20+
struct TodoEntry {
21+
id: u32,
22+
text: String,
23+
}
24+
25+
#[derive(Template)]
26+
#[template(path = "index.html")]
27+
struct IndexTemplate {
28+
entries: Vec<TodoEntry>,
29+
}
30+
31+
#[derive(Error, Debug)]
32+
enum MyError {
33+
#[error("Failed to render HTML")]
34+
AskamaError(#[from] askama::Error),
35+
36+
#[error("Failed to get connection")]
37+
ConnectionPoolError(#[from] r2d2::Error),
38+
39+
#[error("Failed SQL execution")]
40+
SQLiteError(#[from] rusqlite::Error),
41+
}
42+
43+
impl ResponseError for MyError {}
44+
45+
46+
#[get("/")]
47+
async fn index(db: web::Data<Pool<SqliteConnectionManager>>) -> Result<HttpResponse, MyError> {
48+
let conn = db.get()?;
49+
// SQL文を Prepared Statementに変換
50+
let mut statement = conn.prepare("SELECT id, text FROM todo")?;
51+
52+
// Prepared Statement となっている SQL文を実行し、結果をTodoEntryに変換する。
53+
let rows = statement.query_map(params![], |row| {
54+
let id = row.get(0)?;
55+
let text = row.get(1)?;
56+
Ok(TodoEntry {id, text})
57+
})?;
58+
let mut entries = Vec::new();
59+
for row in rows {
60+
entries.push(row?);
61+
}
62+
63+
let html = IndexTemplate { entries };
64+
let response_body = html.render()?;
65+
Ok(HttpResponse::Ok()
66+
.content_type("text/html")
67+
.body(response_body))
68+
}
69+
70+
#[post("/add")]
71+
async fn add_todo(
72+
params: web::Form<AddParams>,
73+
db: web::Data<r2d2::Pool<SqliteConnectionManager>>
74+
) -> Result<HttpResponse, MyError> {
75+
let conn = db.get()?;
76+
conn.execute("INSERT INTO todo (text) VALUES (?)", &[&params.text])?;
77+
Ok(HttpResponse::SeeOther().header(header::LOCATION, "/").finish())
78+
}
79+
80+
#[post("/delete")]
81+
async fn delete_todo(
82+
params: web::Form<DeleteParams>,
83+
db: web::Data<r2d2::Pool<SqliteConnectionManager>>
84+
) -> Result<HttpResponse, MyError> {
85+
let conn = db.get()?;
86+
conn.execute("DELETE FROM todo WHERE id=?", [params.id])?;
87+
Ok(HttpResponse::SeeOther().header(header::LOCATION, "/").finish())
88+
}
89+
90+
91+
#[actix_rt::main]
92+
async fn main() -> Result<(), actix_web::Error> {
93+
let manager = SqliteConnectionManager::file("todo.db");
94+
let pool = Pool::new(manager).expect("Failed to initialize the connection pool.");
95+
let conn = pool
96+
.get()
97+
.expect("Failde to get the connection from the pool.");
98+
conn.execute(
99+
"CREATE TABLE IF NOT EXISTS todo (
100+
id INTEGER PRIMARY KEY AUTOINCREMENT,
101+
text TEXT NOT NULL
102+
)",
103+
params![],
104+
)
105+
.expect("Failed to create a table `todo`.");
106+
107+
//ここでコネクションプールを渡す
108+
HttpServer::new(move || {
109+
App::new()
110+
.service(index)
111+
.service(add_todo)
112+
.service(delete_todo)
113+
.data(pool.clone())
114+
})
115+
.bind("0.0.0.0:8080")?
116+
.run()
117+
.await?;
118+
Ok(())
119+
}

part2/todo/templates/index.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>Todo App</title>
6+
</head>
7+
<body>
8+
<div>
9+
{% for entry in entries %}
10+
<div>
11+
<div>id: {{ entry.id }}, text: {{ entry.text }}</div>
12+
<form action="/delete" method="POST">
13+
<input type="hidden" name="id" value="{{ entry.id }}">
14+
<button>delete</button>
15+
</form>
16+
</div>
17+
{% endfor %}
18+
</div>
19+
20+
<form action="/add" method="POST">
21+
<div>
22+
<input name="text">
23+
</div>
24+
<div>
25+
<button>add</button>
26+
</div>
27+
</form>
28+
</body>
29+
</html>

0 commit comments

Comments
 (0)