1use crate::framework::{
2 middleware::{
3 Middleware,
4 MiddlewareStack,
5 presets::{Cors, TrimStrings}
6 },
7 views::{Metadata, set_global_metadata},
8 database::{self},
9 inertia::InertiaConfig,
10};
11use std::sync::Arc;
12use tokio::sync::RwLock;
13use once_cell::sync::Lazy;
14use crate::app::models;
15use std::path::Path;
16use std::fs;
17use sqlx::sqlite::SqlitePool;
18
19static APP: Lazy<Arc<RwLock<Application>>> = Lazy::new(|| {
21 Arc::new(RwLock::new(Application::new()))
22});
23
24#[derive(Clone)]
26pub struct Application {
27 middleware_stack: MiddlewareStack,
29 middleware_groups: Vec<(String, Vec<Middleware>)>,
31 metadata: Option<Metadata>,
33 inertia_config: Option<InertiaConfig>,
35}
36
37impl Application {
38 pub fn new() -> Self {
39 Self {
40 middleware_stack: MiddlewareStack::new(),
41 middleware_groups: Vec::new(),
42 metadata: None,
43 inertia_config: None,
44 }
45 }
46
47 pub async fn instance() -> Arc<RwLock<Self>> {
49 Arc::clone(&APP)
50 }
51
52 pub async fn middleware<F>(&mut self, configure: F)
54 where
55 F: FnOnce(&mut MiddlewareStack),
56 {
57 configure(&mut self.middleware_stack);
58 }
59
60 pub async fn middleware_groups<F>(&mut self, configure: F)
62 where
63 F: FnOnce(&mut Vec<(String, Vec<Middleware>)>),
64 {
65 configure(&mut self.middleware_groups);
66 }
67
68 pub async fn metadata<F>(&mut self, configure: F)
70 where
71 F: FnOnce() -> Metadata,
72 {
73 self.metadata = Some(configure());
74 if let Some(metadata) = &self.metadata {
75 set_global_metadata(metadata.clone());
76 }
77 }
78
79 pub async fn inertia<F>(&mut self, configure: F)
81 where
82 F: FnOnce() -> InertiaConfig,
83 {
84 self.inertia_config = Some(configure());
85 }
86
87 pub fn middleware_stack(&self) -> MiddlewareStack {
89 self.middleware_stack.clone()
90 }
91
92 pub fn groups(&self) -> Vec<(String, Vec<Middleware>)> {
94 self.middleware_groups.clone()
95 }
96
97 pub fn inertia_config(&self) -> Option<InertiaConfig> {
99 self.inertia_config.clone()
100 }
101}
102
103pub async fn bootstrap() -> Result<Application, Box<dyn std::error::Error>> {
105 models::register_models();
107
108 println!("Initializing database...");
110 let db_config = database::config::DatabaseConfig::from_env();
111 println!("Initializing database at path: {}", db_config.database_path());
112
113 if let Some(parent) = Path::new(&db_config.database_path()).parent() {
115 println!("Creating database directory: {}", parent.display());
116 fs::create_dir_all(parent)?;
117 }
118
119 println!("Connecting to database with URL: {}", db_config.connection_url());
120 let pool = SqlitePool::connect(&db_config.connection_url()).await?;
121 println!("Successfully connected to database");
122
123 println!("Enabling foreign key constraints");
125 sqlx::query("PRAGMA foreign_keys = ON")
126 .execute(&pool)
127 .await?;
128
129 *database::POOL.lock().unwrap() = Some(Arc::new(pool));
131 println!("Database pool initialized successfully");
132
133
134 let app = Application::instance().await;
135 let mut app = app.write().await;
136
137 app.middleware(|stack| {
139 stack.add(Middleware::Cors(Cors::new("*")));
140 stack.add(Middleware::TrimStrings(TrimStrings::new()));
141 }).await;
142
143 app.middleware_groups(|groups| {
145 groups.push(("api".to_string(), vec![
146 Middleware::Cors(Cors::new("*")),
147 Middleware::TrimStrings(TrimStrings::new()),
148 ]));
149 }).await;
150
151 app.metadata(|| {
153 Metadata::new("Ruskit")
154 .with_description("A modern web framework for Rust")
155 .with_keywords("rust, web, framework")
156 .with_author("Your Name")
157 .with_og_title("Ruskit")
158 .with_og_description("A modern web framework for Rust")
159 .with_og_image("https://example.com/og-image.jpg")
160 }).await;
161
162 let app_clone = app.clone();
163 Ok(app_clone)
164}
165
166pub async fn middleware_stack() -> MiddlewareStack {
168 let app = Application::instance().await;
169 let app = app.read().await;
170 app.middleware_stack()
171}
172
173pub async fn middleware_group(name: &str) -> Option<Vec<Middleware>> {
175 let app = Application::instance().await;
176 let app = app.read().await;
177 app.groups()
178 .iter()
179 .find(|(group_name, _)| group_name == name)
180 .map(|(_, middlewares)| middlewares.clone())
181}