Skip to content

Commit

Permalink
feat: add form support and better routing; implement TODO app example
Browse files Browse the repository at this point in the history
  • Loading branch information
m4tx committed Aug 17, 2024
1 parent bd6ae75 commit 1b67ec3
Show file tree
Hide file tree
Showing 29 changed files with 2,377 additions and 138 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Test databases
*.db
*.sqlite3
15 changes: 12 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ members = [
"flareon-admin",
"flareon-auth",
"flareon-macros",
"flareon-orm",
# Examples
"examples/hello-world",
"examples/todo-list",
]
resolver = "2"

Expand All @@ -15,19 +15,28 @@ edition = "2021"
license = "MIT OR Apache-2.0"

[workspace.dependencies]
askama = "0.12.1"
async-trait = "0.1.80"
axum = "0.7.5"
bytes = "1.6.1"
chrono = { version = "0.4.38", features = ["serde"] }
clap = { version = "4.5.8", features = ["derive", "env"] }
convert_case = "0.6.0"
derive_builder = "0.20.0"
derive_more = { version = "1.0.0", features = ["full"] }
env_logger = "0.11.3"
flareon = { path = "flareon" }
flareon_macros = { path = "flareon-macros" }
flareon_orm = { path = "flareon-orm" }
form_urlencoded = "1.2.1"
indexmap = "2.2.6"
itertools = "0.13.0"
log = "0.4.22"
regex = "1.10.5"
sea-query = "0.32.0-rc.1"
sea-query-binder = { version = "0.7.0-rc.1", features = ["sqlx-any", "runtime-tokio"] }
serde = "1.0.203"
slug = "0.1.5"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
tower = "0.4.13"
sqlx = { version = "0.8.0", features = ["runtime-tokio", "sqlite"] }
thiserror = "1.0.61"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
8 changes: 4 additions & 4 deletions examples/hello-world/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::sync::Arc;

use flareon::prelude::{
Body, Error, FlareonApp, FlareonProject, Request, Response, Route, StatusCode,
};
use flareon::prelude::{Body, Error, FlareonApp, FlareonProject, Response, StatusCode};
use flareon::request::Request;
use flareon::router::Route;

fn return_hello(_request: Request) -> Result<Response, Error> {
async fn return_hello(_request: Request) -> Result<Response, Error> {
Ok(Response::new_html(
StatusCode::OK,
Body::fixed("<h1>Hello Flareon!</h1>".as_bytes().to_vec()),
Expand Down
12 changes: 12 additions & 0 deletions examples/todo-list/Cargo.toml
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"
119 changes: 119 additions & 0 deletions examples/todo-list/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::sync::Arc;

use askama::Template;
use flareon::db::query::ExprEq;
use flareon::db::{model, Database, Model};
use flareon::forms::Form;
use flareon::prelude::{Body, Error, FlareonApp, FlareonProject, Response, Route, StatusCode};
use flareon::request::Request;
use flareon::reverse;
use tokio::sync::OnceCell;

#[derive(Debug, Clone)]
#[model]
struct TodoItem {
id: i32,
title: String,
}

#[derive(Debug, Template)]
#[template(path = "index.html")]
struct IndexTemplate<'a> {
request: &'a Request,
todo_items: Vec<TodoItem>,
}

static DB: OnceCell<Database> = OnceCell::const_new();

async fn index(request: Request) -> Result<Response, Error> {
let db = DB.get().unwrap();

let todo_items = TodoItem::objects().all(db).await.unwrap();
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 db = DB.get().unwrap();
TodoItem {
id: 0,
title: todo_form.title,
}
.save(db)
.await
.unwrap();
}

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::<i32>().expect("todo_id is not a number");

{
let db = DB.get().unwrap();
TodoItem::objects()
.filter(<TodoItem as Model>::Fields::ID.eq(todo_id))
.delete(db)
.await
.unwrap();
}

Ok(reverse!(request, "index"))
}

#[tokio::main]
async fn main() {
env_logger::init();

let db = DB
.get_or_init(|| async { Database::new("sqlite::memory:").await.unwrap() })
.await;
db.execute(
r"
CREATE TABLE todo_item (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL
);",
)
.await
.unwrap();

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();
}
28 changes: 28 additions & 0 deletions examples/todo-list/templates/index.html
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 = todo.id %}
<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>
1 change: 1 addition & 0 deletions flareon-admin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[must_use]
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
Expand Down
1 change: 1 addition & 0 deletions flareon-auth/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[must_use]
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
Expand Down
16 changes: 15 additions & 1 deletion flareon-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "flareon-macros"
name = "flareon_macros"
version = "0.1.0"
edition.workspace = true
license.workspace = true
Expand All @@ -8,4 +8,18 @@ description = "Modern web framework focused on speed and ease of use - macros."
[lib]
proc-macro = true

[[test]]
name = "tests"
path = "tests/compile_tests.rs"

[dependencies]
convert_case.workspace = true
darling = "0.20.10"
proc-macro-crate = "3.1.0"
proc-macro2 = "1.0.86"
quote = "1.0.36"
syn = { version = "2.0.74", features = ["full"] }

[dev-dependencies]
flareon.workspace = true
trybuild = { version = "1.0.99", features = ["diff"] }
Loading

0 comments on commit 1b67ec3

Please sign in to comment.