about summary refs log tree commit diff
path: root/kittybox-rs/src/database/mod.rs
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2023-06-15 17:02:39 +0300
committerVika <vika@fireburn.ru>2023-06-15 17:02:39 +0300
commiteca7687439c2b6f804603de75501b6737a82e5a2 (patch)
treea4715e0b8d3d63ee8b77830670c473b83a70031e /kittybox-rs/src/database/mod.rs
parent59f3e2d43d30642e4242039ce3ab934961e69602 (diff)
Database: use newtypes to represent settings
This allows much for a cleaner and idiomatic settings interface.
Diffstat (limited to 'kittybox-rs/src/database/mod.rs')
-rw-r--r--kittybox-rs/src/database/mod.rs92
1 files changed, 73 insertions, 19 deletions
diff --git a/kittybox-rs/src/database/mod.rs b/kittybox-rs/src/database/mod.rs
index 24cff38..2039ac0 100644
--- a/kittybox-rs/src/database/mod.rs
+++ b/kittybox-rs/src/database/mod.rs
@@ -1,6 +1,5 @@
 #![warn(missing_docs)]
 use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
 
 mod file;
 pub use crate::database::file::FileStorage;
@@ -11,6 +10,8 @@ pub use crate::database::memory::MemoryStorage;
 
 pub use kittybox_util::MicropubChannel;
 
+use self::settings::Setting;
+
 /// Enum representing different errors that might occur during the database query.
 #[derive(Debug, Clone, Copy)]
 pub enum ErrorKind {
@@ -33,17 +34,68 @@ pub enum ErrorKind {
     Other,
 }
 
-/// Enum representing settings that might be stored in the site's database.
-#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
-#[serde(rename_all = "snake_case")]
-pub enum Settings {
-    /// The name of the website -- displayed in the header and the browser titlebar.
-    SiteName,
-}
+/// Settings that can be stored in the database.
+pub mod settings {
+    mod private {
+        pub trait Sealed {}
+    }
+    /*#[derive(serde::Deserialize, serde::Serialize, Default)]
+    pub struct Settings {
+        pub site_name: SiteName,
+        pub webring: Webring
+    }
+    impl From<Settings> for SiteName {
+        fn from(settings: Settings) -> Self {
+            settings.site_name
+        }
+    }*/
+
+    /// A trait for various settings that should be contained here.
+    ///
+    /// **Note**: this trait is sealed to prevent external
+    /// implementations, as it wouldn't make sense to add new settings
+    /// that aren't used by Kittybox itself.
+    pub trait Setting<'de>: private::Sealed + std::fmt::Debug + Default + Clone + serde::Serialize + serde::de::DeserializeOwned + /*From<Settings> +*/ Send + Sync {
+        type Data: std::fmt::Debug + Send + Sync;
+        const ID: &'static str;
+
+        /// Unwrap the setting type, returning owned data contained within.
+        fn into_inner(self) -> Self::Data;
+        /// Create a new instance of this type containing certain data.
+        fn new(data: Self::Data) -> Self;
+    }
+
+    /// A website's title, shown in the header.
+    #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
+    pub struct SiteName(String);
+    impl Default for SiteName {
+        fn default() -> Self {
+            Self("Kittybox".to_string())
+        }
+    }
+    impl AsRef<str> for SiteName {
+        fn as_ref(&self) -> &str {
+            self.0.as_str()
+        }
+    }
+    impl private::Sealed for SiteName {}
+    impl Setting<'_> for SiteName {
+        type Data = String;
+        const ID: &'static str = "site_name";
+
+        fn into_inner(self) -> String {
+            self.0
+        }
+        fn new(data: Self::Data) -> Self {
+            Self(data)
+        }
+    }
+    impl SiteName {
+        fn from_str(data: &str) -> Self {
+            Self(data.to_owned())
+        }
+    }
 
-impl std::string::ToString for Settings {
-    fn to_string(&self) -> String {
-        serde_variant::to_variant_name(self).unwrap().to_string()
     }
 }
 
@@ -248,14 +300,16 @@ pub trait Storage: std::fmt::Debug + Clone + Send + Sync {
     async fn delete_post(&self, url: &'_ str) -> Result<()>;
 
     /// Gets a setting from the setting store and passes the result.
-    async fn get_setting(&self, setting: Settings, user: &'_ str) -> Result<String>;
+    async fn get_setting<S: Setting<'a>, 'a>(&'_ self, user: &'_ str) -> Result<S>;
 
     /// Commits a setting to the setting store.
-    async fn set_setting(&self, setting: Settings, user: &'_ str, value: &'_ str) -> Result<()>;
+    async fn set_setting<S: Setting<'a>, 'a>(&self, user: &'_ str, value: S::Data) -> Result<()>;
 }
 
 #[cfg(test)]
 mod tests {
+    use super::settings;
+
     use super::{MicropubChannel, Storage};
     use serde_json::json;
 
@@ -413,18 +467,18 @@ mod tests {
 
     async fn test_settings<Backend: Storage>(backend: Backend) {
         backend
-            .set_setting(
-                crate::database::Settings::SiteName,
+            .set_setting::<settings::SiteName>(
                 "https://fireburn.ru/",
-                "Vika's Hideout",
+                "Vika's Hideout".to_owned()
             )
             .await
             .unwrap();
         assert_eq!(
             backend
-                .get_setting(crate::database::Settings::SiteName, "https://fireburn.ru/")
-                .await
-                .unwrap(),
+            .get_setting::<settings::SiteName>("https://fireburn.ru/")
+            .await
+            .unwrap()
+            .as_ref(),
             "Vika's Hideout"
         );
     }