-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add form support and better routing; implement TODO app example
- Loading branch information
Showing
27 changed files
with
1,996 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "example-link-shortener" | ||
version = "0.1.0" | ||
publish = false | ||
description = "Link Shortener - Flareon example." | ||
edition = "2021" | ||
|
||
[dependencies] | ||
askama = "0.12.1" | ||
env_logger = "0.11.5" | ||
flareon = { path = "../../flareon" } | ||
sea-query = "0.32.0-rc.1" | ||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
mod models; | ||
|
||
use std::sync::Arc; | ||
|
||
use askama::Template; | ||
use flareon::db::{Database, Model}; | ||
use flareon::forms::Form; | ||
use flareon::prelude::{Body, Error, FlareonApp, FlareonProject, Response, Route, StatusCode}; | ||
use flareon::request::Request; | ||
use flareon::reverse; | ||
use sea_query::Table; | ||
use tokio::sync::RwLock; | ||
|
||
use crate::models::Link; | ||
// #[derive(Debug, Clone)] | ||
// struct TodoItem { | ||
// title: String, | ||
// } | ||
// | ||
// #[derive(Debug, Template)] | ||
// #[template(path = "index.html")] | ||
// struct IndexTemplate<'a> { | ||
// request: &'a Request, | ||
// todo_items: Vec<TodoItem>, | ||
// } | ||
|
||
// static TODOS: RwLock<Vec<TodoItem>> = RwLock::const_new(Vec::new()); | ||
|
||
// async fn index(request: Request) -> Result<Response, Error> { | ||
// let todo_items = (*TODOS.read().await).clone(); | ||
// let index_template = IndexTemplate { | ||
// request: &request, | ||
// todo_items, | ||
// }; | ||
// let rendered = index_template.render().unwrap(); | ||
// | ||
// Ok(Response::new_html( | ||
// StatusCode::OK, | ||
// Body::fixed(rendered.as_bytes().to_vec()), | ||
// )) | ||
// } | ||
// | ||
// #[derive(Debug, Form)] | ||
// struct TodoForm { | ||
// #[form(opt(max_length = 100))] | ||
// title: String, | ||
// } | ||
// | ||
// async fn add_todo(mut request: Request) -> Result<Response, Error> { | ||
// let todo_form = TodoForm::from_request(&mut request).await.unwrap(); | ||
// | ||
// { | ||
// let mut todos = TODOS.write().await; | ||
// todos.push(TodoItem { | ||
// title: todo_form.title, | ||
// }); | ||
// } | ||
// | ||
// Ok(reverse!(request, "index")) | ||
// } | ||
// | ||
// async fn remove_todo(request: Request) -> Result<Response, Error> { | ||
// let todo_id = request.path_param("todo_id").expect("todo_id not found"); | ||
// let todo_id = todo_id.parse::<usize>().expect("todo_id is not a number"); | ||
// | ||
// { | ||
// let mut todos = TODOS.write().await; | ||
// todos.remove(todo_id); | ||
// } | ||
// | ||
// Ok(reverse!(request, "index")) | ||
// } | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
env_logger::init(); | ||
|
||
// let todo_app = FlareonApp::builder() | ||
// .urls([ | ||
// Route::with_handler_and_name("/", Arc::new(Box::new(index)), | ||
// "index"), Route::with_handler_and_name("/todos/add", | ||
// Arc::new(Box::new(add_todo)), "add-todo"), | ||
// Route::with_handler_and_name( | ||
// "/todos/:todo_id/remove", | ||
// Arc::new(Box::new(remove_todo)), | ||
// "remove-todo", | ||
// ), | ||
// ]) | ||
// .build() | ||
// .unwrap(); | ||
// | ||
// let todo_project = FlareonProject::builder() | ||
// .register_app_with_views(todo_app, "") | ||
// .build() | ||
// .unwrap(); | ||
// | ||
// flareon::run(todo_project, "127.0.0.1:8000").await.unwrap(); | ||
|
||
let db = Database::new("sqlite:db.sqlite3?mode=rwc&cache=shared") | ||
.await | ||
.unwrap(); | ||
|
||
db.execute( | ||
r"CREATE TABLE IF NOT EXISTS link ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
slug TEXT NOT NULL, | ||
url TEXT NOT NULL, | ||
visits INTEGER NOT NULL DEFAULT 0 | ||
)", | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let mut link = Link { | ||
id: 1, | ||
slug: "test2".to_string(), | ||
url: "https://example.com".to_string(), | ||
visits: 0, | ||
}; | ||
link.save(&db).await.unwrap(); | ||
link.save(&db).await.unwrap(); | ||
|
||
db.close().await.unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
pub struct Link { | ||
pub id: i32, | ||
pub slug: String, | ||
pub url: String, | ||
pub visits: i32, | ||
} | ||
|
||
impl ::flareon::db::Model for Link { | ||
const COLUMNS: &'static [::flareon::db::Column] = &[ | ||
::flareon::db::Column::new(::flareon::db::Identifier::new("id")).auto(true), | ||
::flareon::db::Column::new(::flareon::db::Identifier::new("slug")), | ||
::flareon::db::Column::new(::flareon::db::Identifier::new("url")), | ||
::flareon::db::Column::new(::flareon::db::Identifier::new("visits")), | ||
]; | ||
const TABLE_NAME: &'static str = "link"; | ||
|
||
fn from_db(db_row: ::flareon::db::Row) -> Self { | ||
Self { | ||
id: db_row.get::<i32>(0), | ||
slug: db_row.get::<String>(1), | ||
url: db_row.get::<String>(2), | ||
visits: db_row.get::<i32>(3), | ||
} | ||
} | ||
|
||
fn get_values(&self, columns: &[usize]) -> Vec<&dyn ::flareon::db::ValueRef> { | ||
columns | ||
.iter() | ||
.map(|&column| match column { | ||
0 => &self.id as &dyn ::flareon::db::ValueRef, | ||
1 => &self.slug as &dyn ::flareon::db::ValueRef, | ||
2 => &self.url as &dyn ::flareon::db::ValueRef, | ||
3 => &self.visits as &dyn ::flareon::db::ValueRef, | ||
_ => panic!("Unknown column index: {}", column), | ||
}) | ||
.collect() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{% let request = request %} | ||
|
||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>TODO List</title> | ||
</head> | ||
<body> | ||
<h1>TODO List</h1> | ||
<form id="todo-form" action="{{ flareon::reverse_str!(request, "add-todo") }}" method="post"> | ||
<input type="text" id="title" name="title" placeholder="Enter a new TODO" required> | ||
<button type="submit">Add TODO</button> | ||
</form> | ||
<ul id="todo-list"> | ||
{% for todo in todo_items %} | ||
<li> | ||
{% let todo_id = loop.index0 %} | ||
<form action="{{ flareon::reverse_str!(request, "remove-todo", "todo_id" => todo_id) }}" method="post"> | ||
<span>{{ todo.title }}</span> | ||
<button type="submit">Remove</button> | ||
</form> | ||
</li> | ||
{% endfor %} | ||
</ul> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "example-todo-list" | ||
version = "0.1.0" | ||
publish = false | ||
description = "TODO List - Flareon example." | ||
edition = "2021" | ||
|
||
[dependencies] | ||
askama = "0.12.1" | ||
flareon = { path = "../../flareon" } | ||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] } | ||
env_logger = "0.11.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
use std::sync::Arc; | ||
|
||
use askama::Template; | ||
use flareon::forms::Form; | ||
use flareon::prelude::{Body, Error, FlareonApp, FlareonProject, Response, Route, StatusCode}; | ||
use flareon::request::Request; | ||
use flareon::reverse; | ||
use tokio::sync::RwLock; | ||
|
||
#[derive(Debug, Clone)] | ||
struct TodoItem { | ||
title: String, | ||
} | ||
|
||
#[derive(Debug, Template)] | ||
#[template(path = "index.html")] | ||
struct IndexTemplate<'a> { | ||
request: &'a Request, | ||
todo_items: Vec<TodoItem>, | ||
} | ||
|
||
static TODOS: RwLock<Vec<TodoItem>> = RwLock::const_new(Vec::new()); | ||
|
||
async fn index(request: Request) -> Result<Response, Error> { | ||
let todo_items = (*TODOS.read().await).clone(); | ||
let index_template = IndexTemplate { | ||
request: &request, | ||
todo_items, | ||
}; | ||
let rendered = index_template.render().unwrap(); | ||
|
||
Ok(Response::new_html( | ||
StatusCode::OK, | ||
Body::fixed(rendered.as_bytes().to_vec()), | ||
)) | ||
} | ||
|
||
#[derive(Debug, Form)] | ||
struct TodoForm { | ||
#[form(opt(max_length = 100))] | ||
title: String, | ||
} | ||
|
||
async fn add_todo(mut request: Request) -> Result<Response, Error> { | ||
let todo_form = TodoForm::from_request(&mut request).await.unwrap(); | ||
|
||
{ | ||
let mut todos = TODOS.write().await; | ||
todos.push(TodoItem { | ||
title: todo_form.title, | ||
}); | ||
} | ||
|
||
Ok(reverse!(request, "index")) | ||
} | ||
|
||
async fn remove_todo(request: Request) -> Result<Response, Error> { | ||
let todo_id = request.path_param("todo_id").expect("todo_id not found"); | ||
let todo_id = todo_id.parse::<usize>().expect("todo_id is not a number"); | ||
|
||
{ | ||
let mut todos = TODOS.write().await; | ||
todos.remove(todo_id); | ||
} | ||
|
||
Ok(reverse!(request, "index")) | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
env_logger::init(); | ||
|
||
let todo_app = FlareonApp::builder() | ||
.urls([ | ||
Route::with_handler_and_name("/", Arc::new(Box::new(index)), "index"), | ||
Route::with_handler_and_name("/todos/add", Arc::new(Box::new(add_todo)), "add-todo"), | ||
Route::with_handler_and_name( | ||
"/todos/:todo_id/remove", | ||
Arc::new(Box::new(remove_todo)), | ||
"remove-todo", | ||
), | ||
]) | ||
.build() | ||
.unwrap(); | ||
|
||
let todo_project = FlareonProject::builder() | ||
.register_app_with_views(todo_app, "") | ||
.build() | ||
.unwrap(); | ||
|
||
flareon::run(todo_project, "127.0.0.1:8000").await.unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{% let request = request %} | ||
|
||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>TODO List</title> | ||
</head> | ||
<body> | ||
<h1>TODO List</h1> | ||
<form id="todo-form" action="{{ flareon::reverse_str!(request, "add-todo") }}" method="post"> | ||
<input type="text" id="title" name="title" placeholder="Enter a new TODO" required> | ||
<button type="submit">Add TODO</button> | ||
</form> | ||
<ul id="todo-list"> | ||
{% for todo in todo_items %} | ||
<li> | ||
{% let todo_id = loop.index0 %} | ||
<form action="{{ flareon::reverse_str!(request, "remove-todo", "todo_id" => todo_id) }}" method="post"> | ||
<span>{{ todo.title }}</span> | ||
<button type="submit">Remove</button> | ||
</form> | ||
</li> | ||
{% endfor %} | ||
</ul> | ||
</body> | ||
</html> |
Oops, something went wrong.