Skip to content

Commit

Permalink
feat: implement flareon::main macro for easy framework bootstrapping
Browse files Browse the repository at this point in the history
This is the first step in making Flareon an actual "framework" rather
than just a library. This macro currently doesn't do much now, but
eventually we'd like to have an entire CLI that would be automatically
generated for each Flareon project and would allow to do some common
operations (run server, run migrations, copy static files to a
directory, etc.), as well as allow Flareon users to define their own
ones.
  • Loading branch information
m4tx committed Nov 24, 2024
1 parent e718f8b commit 56fd510
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 100 deletions.
5 changes: 0 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions examples/admin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ description = "Admin panel - Flareon example."
edition = "2021"

[dependencies]
env_logger = "0.11.5"
flareon = { path = "../../flareon" }
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
13 changes: 4 additions & 9 deletions examples/admin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ impl FlareonApp for HelloApp {
}
}

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

#[flareon::main]
async fn main() -> flareon::Result<FlareonProject> {
let flareon_project = FlareonProject::builder()
.config(
ProjectConfig::builder()
Expand All @@ -57,10 +55,7 @@ async fn main() {
.middleware_with_context(StaticFilesMiddleware::from_app_context)
.middleware(SessionMiddleware::new())
.build()
.await
.unwrap();
.await?;

flareon::run(flareon_project, "127.0.0.1:8000")
.await
.unwrap();
Ok(flareon_project)
}
1 change: 0 additions & 1 deletion examples/hello-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ edition = "2021"

[dependencies]
flareon = { path = "../../flareon" }
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
11 changes: 4 additions & 7 deletions examples/hello-world/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,12 @@ impl FlareonApp for HelloApp {
}
}

#[tokio::main]
async fn main() {
#[flareon::main]
async fn main() -> flareon::Result<FlareonProject> {
let flareon_project = FlareonProject::builder()
.register_app_with_views(HelloApp, "")
.build()
.await
.unwrap();
.await?;

flareon::run(flareon_project, "127.0.0.1:8000")
.await
.unwrap();
Ok(flareon_project)
}
1 change: 0 additions & 1 deletion examples/sessions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ edition = "2021"
[dependencies]
askama = "0.12.1"
flareon = { path = "../../flareon" }
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
11 changes: 4 additions & 7 deletions examples/sessions/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,13 @@ impl FlareonApp for HelloApp {
}
}

#[tokio::main]
async fn main() {
#[flareon::main]
async fn main() -> flareon::Result<FlareonProject> {
let flareon_project = FlareonProject::builder()
.register_app_with_views(HelloApp, "")
.middleware(SessionMiddleware::new())
.build()
.await
.unwrap();
.await?;

flareon::run(flareon_project, "127.0.0.1:8000")
.await
.unwrap();
Ok(flareon_project)
}
1 change: 0 additions & 1 deletion examples/todo-list/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ edition = "2021"
askama = "0.12.1"
flareon = { path = "../../flareon" }
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
env_logger = "0.11.5"
59 changes: 32 additions & 27 deletions examples/todo-list/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
mod migrations;

use askama::Template;
use flareon::db::migrations::MigrationEngine;
use flareon::db::{model, query, Database, Model};
use flareon::config::{DatabaseConfig, ProjectConfig};
use flareon::db::migrations::DynMigration;
use flareon::db::{model, query, Model};
use flareon::forms::Form;
use flareon::request::{Request, RequestExt};
use flareon::response::{Response, ResponseExt};
use flareon::router::{Route, Router};
use flareon::{reverse, Body, FlareonApp, FlareonProject, StatusCode};
use tokio::sync::OnceCell;

#[derive(Debug, Clone)]
#[model]
Expand All @@ -24,12 +24,8 @@ struct IndexTemplate<'a> {
todo_items: Vec<TodoItem>,
}

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

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

let todo_items = TodoItem::objects().all(db).await?;
let todo_items = TodoItem::objects().all(request.db()).await?;
let index_template = IndexTemplate {
request: &request,
todo_items,
Expand All @@ -49,12 +45,11 @@ async fn add_todo(mut request: Request) -> flareon::Result<Response> {
let todo_form = TodoForm::from_request(&mut request).await?.unwrap();

{
let db = DB.get().unwrap();
TodoItem {
id: 0,
title: todo_form.title,
}
.save(db)
.save(request.db())
.await?;
}

Expand All @@ -69,8 +64,9 @@ async fn remove_todo(request: Request) -> flareon::Result<Response> {
let todo_id = todo_id.parse::<i32>().expect("todo_id is not a number");

{
let db = DB.get().unwrap();
query!(TodoItem, $id == todo_id).delete(db).await?;
query!(TodoItem, $id == todo_id)
.delete(request.db())
.await?;
}

Ok(reverse!(request, "index"))
Expand All @@ -83,6 +79,16 @@ impl FlareonApp for TodoApp {
"todo-app"
}

fn migrations(&self) -> Vec<Box<dyn DynMigration>> {
// TODO: this is way too complicated for the user-facing API
#[allow(trivial_casts)]
migrations::MIGRATIONS
.iter()
.copied()
.map(|x| Box::new(x) as Box<dyn DynMigration>)
.collect()
}

fn router(&self) -> Router {
Router::with_urls([
Route::with_handler_and_name("/", index, "index"),
Expand All @@ -92,23 +98,22 @@ impl FlareonApp for TodoApp {
}
}

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

let db = DB
.get_or_init(|| async { Database::new("sqlite::memory:").await.unwrap() })
.await;
MigrationEngine::new(migrations::MIGRATIONS.iter().copied())
.run(db)
.await
.unwrap();

#[flareon::main]
async fn main() -> flareon::Result<FlareonProject> {
let todo_project = FlareonProject::builder()
.config(
ProjectConfig::builder()
.database_config(
DatabaseConfig::builder()
.url("sqlite::memory:")
.build()
.unwrap(),
)
.build(),
)
.register_app_with_views(TodoApp, "")
.build()
.await
.unwrap();
.await?;

flareon::run(todo_project, "127.0.0.1:8080").await.unwrap();
Ok(todo_project)
}
10 changes: 10 additions & 0 deletions flareon-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod dbtest;
mod form;
mod main_fn;
mod model;
mod query;

Expand All @@ -12,6 +13,7 @@ use syn::{parse_macro_input, ItemFn};

use crate::dbtest::fn_to_dbtest;
use crate::form::impl_form_for_struct;
use crate::main_fn::fn_to_flareon_main;
use crate::model::impl_model_for_struct;
use crate::query::{query_to_tokens, Query};

Expand Down Expand Up @@ -122,6 +124,14 @@ pub fn dbtest(_args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}

#[proc_macro_attribute]
pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
let fn_input = parse_macro_input!(input as ItemFn);
fn_to_flareon_main(fn_input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}

pub(crate) fn flareon_ident() -> proc_macro2::TokenStream {
let flareon_crate = crate_name("flareon").expect("flareon is not present in `Cargo.toml`");
match flareon_crate {
Expand Down
40 changes: 40 additions & 0 deletions flareon-macros/src/main_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::ItemFn;

use crate::flareon_ident;

pub(super) fn fn_to_flareon_main(main_function_decl: ItemFn) -> syn::Result<TokenStream> {
let mut new_main_decl = main_function_decl.clone();
new_main_decl.sig.ident =
syn::Ident::new("__flareon_main", main_function_decl.sig.ident.span());

if !main_function_decl.sig.inputs.is_empty() {
return Err(syn::Error::new_spanned(
main_function_decl.sig.inputs,
"flareon::main function must have zero arguments",
));
}

let crate_name = flareon_ident();
let result = quote! {
fn main() {
let body = async {
let project: #crate_name::FlareonProject = __flareon_main().await.unwrap();
#crate_name::run_cli(project).await.unwrap();

#new_main_decl
};
#[allow(clippy::expect_used)]
{
return #crate_name::__private::tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(body);
}
}

};
Ok(result)
}
Loading

0 comments on commit 56fd510

Please sign in to comment.