create_rust_app/
lib.rs

1#[cfg(all(feature = "backend_actix-web", feature = "backend_poem"))]
2compile_error!(
3    "feature \"backend_actix-web\" and feature \"backend_poem\" cannot be enabled at the same time"
4);
5
6#[cfg(all(feature = "database_sqlite", feature = "database_postgres"))]
7compile_error!(
8    "feature \"database_sqlite\" and feature \"database_postgres\" cannot be enabled at the same time"
9);
10
11// #[cfg(not(any(feature = "backend_poem", feature = "backend_actix-web")))]
12// compile_error!(
13//     "Please enable one of the backend features (options: 'backend_actix-web', 'backend-poem')"
14// );
15
16mod util;
17pub use util::*;
18
19#[macro_use]
20extern crate diesel;
21
22#[cfg(feature = "plugin_auth")]
23pub mod auth;
24
25#[cfg(feature = "plugin_tasks")]
26pub mod tasks;
27
28#[cfg(all(feature = "plugin_dev", debug_assertions))]
29pub mod dev;
30#[cfg(all(feature = "plugin_dev", debug_assertions))]
31pub use dev::setup_development;
32
33mod database;
34pub use database::{Connection, Database, Pool};
35
36#[cfg(feature = "backend_poem")]
37mod logger;
38#[allow(deprecated)] // deprecated; we're going to roll out better logging soon. Use your own tracing setup for now!
39#[cfg(feature = "backend_poem")]
40pub use logger::Logger as PoemLogger;
41
42#[cfg(feature = "plugin_storage")]
43mod storage;
44#[cfg(feature = "plugin_storage")]
45pub use storage::{Attachment, AttachmentBlob, AttachmentData, Storage};
46
47mod mailer;
48pub use mailer::Mailer;
49#[cfg(feature = "plugin_auth")]
50pub use mailer::{DefaultMailTemplates, EmailTemplates};
51
52// #[cfg(debug_assertions)]
53// #[macro_use]
54// extern crate diesel_migrations;
55
56#[derive(Clone)]
57pub struct AppConfig {
58    // where the app is hosted; for example: create-rust-app.dev:3000
59    pub app_url: String,
60}
61
62#[derive(Clone)]
63/// Struct that holds shared data for the application
64pub struct AppData {
65    /// wrapper for SMTP mailing server accessed by chosen web framework
66    ///
67    /// see [`Mailer`]
68    pub mailer: Mailer,
69    /// db agnostic wrapper for databases accessed by chosen web framework
70    ///
71    /// see [`Database`]
72    pub database: Database,
73    #[cfg(feature = "plugin_storage")]
74    /// wrapper for Amazon S3 cloud file storage service accessed by chosen web framework
75    ///
76    /// see [`Storage`]
77    pub storage: Storage,
78}
79
80#[cfg(feature = "plugin_auth")]
81impl AppData {
82    #[must_use]
83    pub fn with_custom_email_templates<T: EmailTemplates + 'static>(
84        mut self,
85        templates: T,
86    ) -> Self {
87        self.mailer = Mailer::new(Box::new(templates));
88        self
89    }
90}
91
92#[cfg(debug_assertions)]
93fn load_env_vars() {
94    static START: std::sync::Once = std::sync::Once::new();
95
96    START.call_once(|| {
97        dotenv::dotenv().unwrap_or_else(|_| {
98            panic!("ERROR: Could not load environment variables from dotenv file");
99        });
100    });
101}
102
103/// ensures required environment variables are present,
104///  
105/// initialize a [`Mailer`], [`Database`], and [`Storage`] (is `Storage` plugin was enabled ("`plugin_storage`" feature enabled))
106///
107/// and wraps them in a [`AppData`] struct that is then returned
108///
109/// # Panics
110///
111/// Panics if required environment variables are not present
112/// TODO: should we panic here? wouldn't it be better to return a Result and let the user handle the error?
113#[must_use]
114pub fn setup() -> AppData {
115    // Only load dotenv in development
116    #[cfg(debug_assertions)]
117    {
118        load_env_vars();
119
120        // #[cfg(feature = "backend_actix-web")]
121        // env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
122    }
123
124    #[cfg(feature = "plugin_auth")]
125    assert!(
126        std::env::var("SECRET_KEY").is_ok(),
127        "No SECRET_KEY environment variable set!"
128    );
129
130    assert!(
131        std::env::var("DATABASE_URL").is_ok(),
132        "No DATABASE_URL environment variable set!"
133    );
134
135    AppData {
136        mailer: Mailer::default(),
137        database: Database::new(),
138        #[cfg(feature = "plugin_storage")]
139        storage: Storage::new(),
140    }
141}
142
143#[cfg(feature = "backend_poem")]
144/// TODO: documentation
145pub async fn not_found(_: poem::error::NotFoundError) -> poem::Response {
146    let json = serde_json::json!({
147        "success": false,
148        "message": "Invalid endpoint"
149    });
150
151    poem::Response::builder()
152        .status(poem::http::StatusCode::NOT_FOUND)
153        .header("Content-Type", "application/json")
154        .body(json.to_string())
155}