about summary refs log tree commit diff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs118
1 files changed, 116 insertions, 2 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 04b3298..1cc01c2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,17 @@
 #![forbid(unsafe_code)]
 #![warn(clippy::todo)]
 
+use std::sync::Arc;
+
+use axum::extract::FromRef;
+use axum_extra::extract::cookie::Key;
+use database::{FileStorage, PostgresStorage, Storage};
+use indieauth::backend::{AuthBackend, FileBackend as FileAuthBackend};
+use kittybox_util::queue::{JobItem, JobQueue};
+use media::storage::{MediaStore, file::FileStore as FileMediaStore};
+use tokio::{sync::Mutex, task::JoinSet};
+use webmentions::queue::{PostgresJobItem, PostgresJobQueue};
+
 /// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database.
 pub mod database;
 pub mod frontend;
@@ -9,6 +20,110 @@ pub mod micropub;
 pub mod indieauth;
 pub mod webmentions;
 pub mod login;
+//pub mod admin;
+
+#[derive(Clone)]
+pub struct AppState<A, S, M, Q>
+where
+A: AuthBackend + Sized + 'static,
+S: Storage + Sized + 'static,
+M: MediaStore + Sized + 'static,
+Q: JobQueue<webmentions::Webmention> + Sized
+{
+    pub auth_backend: A,
+    pub storage: S,
+    pub media_store: M,
+    pub job_queue: Q,
+    pub http: reqwest::Client,
+    pub background_jobs: Arc<Mutex<JoinSet<()>>>,
+    pub cookie_key: Key
+}
+
+// This is really regrettable, but I can't write:
+//
+// ```compile-error
+// impl <A, S, M> FromRef<AppState<A, S, M>> for A
+// where A: AuthBackend, S: Storage, M: MediaStore {
+//     fn from_ref(input: &AppState<A, S, M>) -> A {
+//         input.auth_backend.clone()
+//     }
+// }
+// ```
+//
+// ...because of the orphan rule.
+//
+// I wonder if this would stifle external implementations. I think it
+// shouldn't, because my AppState type is generic, and since the
+// target type is local, the orphan rule will not kick in. You just
+// have to repeat this magic invocation.
+
+impl<S, M, Q> FromRef<AppState<Self, S, M, Q>> for FileAuthBackend
+// where S: Storage, M: MediaStore
+where S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<Self, S, M, Q>) -> Self {
+        input.auth_backend.clone()
+    }
+}
+
+impl<A, M, Q> FromRef<AppState<A, Self, M, Q>> for PostgresStorage
+// where A: AuthBackend, M: MediaStore
+where A: AuthBackend, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, Self, M, Q>) -> Self {
+        input.storage.clone()
+    }
+}
+
+impl<A, M, Q> FromRef<AppState<A, Self, M, Q>> for FileStorage
+where A: AuthBackend, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, Self, M, Q>) -> Self {
+        input.storage.clone()
+    }
+}
+
+impl<A, S, Q> FromRef<AppState<A, S, Self, Q>> for FileMediaStore
+// where A: AuthBackend, S: Storage
+where A: AuthBackend, S: Storage, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, S, Self, Q>) -> Self {
+        input.media_store.clone()
+    }
+}
+
+impl<A, S, M, Q> FromRef<AppState<A, S, M, Q>> for Key
+where A: AuthBackend, S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, S, M, Q>) -> Self {
+        input.cookie_key.clone()
+    }
+}
+
+impl<A, S, M, Q> FromRef<AppState<A, S, M, Q>> for reqwest::Client
+where A: AuthBackend, S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, S, M, Q>) -> Self {
+        input.http.clone()
+    }
+}
+
+impl<A, S, M, Q> FromRef<AppState<A, S, M, Q>> for Arc<Mutex<JoinSet<()>>>
+where A: AuthBackend, S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention>
+{
+    fn from_ref(input: &AppState<A, S, M, Q>) -> Self {
+        input.background_jobs.clone()
+    }
+}
+
+#[cfg(feature = "sqlx")]
+impl<A, S, M> FromRef<AppState<A, S, M, Self>> for PostgresJobQueue<webmentions::Webmention>
+where A: AuthBackend, S: Storage, M: MediaStore
+{
+    fn from_ref(input: &AppState<A, S, M, Self>) -> Self {
+        input.job_queue.clone()
+    }
+}
 
 pub mod companion {
     use std::{collections::HashMap, sync::Arc};
@@ -52,8 +167,7 @@ pub mod companion {
         }
     }
 
-    #[must_use]
-    pub fn router() -> axum::Router {
+    pub fn router<St: Clone + Send + Sync + 'static>() -> axum::Router<St> {
         let resources: ResourceTable = {
             let mut map = HashMap::new();