From 47f14a3fbe49f991336859d33b8d8142a509a65f Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Thu, 26 Jan 2023 18:35:35 +0100
Subject: [PATCH] AMALIB: Add a way to override kara informations

---
 src/rust/amalib/src/db_cache.rs    |   4 +-
 src/rust/amalib/src/db_kara.rs     | 155 +++++++++++++++++++++++++++++
 src/rust/amalib/src/db_objects.rs  | 142 --------------------------
 src/rust/amalib/src/db_playlist.rs |  56 +++++++++++
 src/rust/amalib/src/db_tags.rs     |   2 +-
 src/rust/amalib/src/lib.rs         |   6 +-
 6 files changed, 218 insertions(+), 147 deletions(-)
 create mode 100644 src/rust/amalib/src/db_kara.rs
 delete mode 100644 src/rust/amalib/src/db_objects.rs
 create mode 100644 src/rust/amalib/src/db_playlist.rs

diff --git a/src/rust/amalib/src/db_cache.rs b/src/rust/amalib/src/db_cache.rs
index 776c713b..755235fa 100644
--- a/src/rust/amalib/src/db_cache.rs
+++ b/src/rust/amalib/src/db_cache.rs
@@ -23,7 +23,7 @@ pub struct AmaDB {
     playlists: LruCache<SmallString, Playlist>,
 
     /// The list of overrides, indexed by the local id of the kara.
-    overrides: HashMap<i64, KaraOverride>,
+    overrides: HashMap<i64, KaraInfoOverride>,
 
     /// The conversion list for `local id <-> repo id`. Overrides are declared
     /// with the repo ids because it's the only thing that every client have in
@@ -74,7 +74,7 @@ impl AmaDB {
     /// Add an override to the override list. If a previous override is present
     /// for the specified ID, we merge the new override into the previous one.
     /// If the kara is present in the cache, we generate the patched version.
-    pub fn add_kara_override(&mut self, _id: KaraId, _override: KaraOverride) {
+    pub fn add_kara_override(&mut self, _id: KaraId, _override: KaraInfoOverride) {
         todo!()
     }
 
diff --git a/src/rust/amalib/src/db_kara.rs b/src/rust/amalib/src/db_kara.rs
new file mode 100644
index 00000000..bda0b88c
--- /dev/null
+++ b/src/rust/amalib/src/db_kara.rs
@@ -0,0 +1,155 @@
+//! Describes karas.
+
+use crate::*;
+
+/// Represent the possible types for a song. The custom field is here for
+/// flexibility, we box it so it's not too big and because it won't be common.
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub enum SongType {
+    OP,
+    ED,
+    IS,
+    MV,
+    OT,
+    Custom(Box<String>),
+}
+
+/// Represent the possible origins for a song. The custom field is here for
+/// flexibility, we box it so it's not too big and because it won't be common.
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum SongOrigin {
+    Anime,
+    VN,
+    Game,
+    Music,
+    Autre,
+    Custom(Box<String>),
+}
+
+/// Represent a global id for a kara, the pair (database, id).
+#[derive(Debug, Deserialize, Serialize, Getters, CopyGetters)]
+pub struct KaraId {
+    #[getset(get = "pub")]
+    repo: SmallString,
+
+    #[getset(get_copy = "pub")]
+    id: i64,
+}
+
+/// A list of informations about a kara. This is the data from lektor. When
+/// queried we patch it with the overrides given by the user.
+#[derive(Debug, Getters)]
+#[getset(get = "pub")]
+pub struct KaraInfo {
+    song_title: SmallString,
+    song_source: SmallString,
+    song_type: SongType,
+    song_origin: SongOrigin,
+    languages: HashSet<SmallString>,
+    kara_makers: HashSet<SmallString>,
+
+    #[getset(skip)]
+    tags: SimpleTagSystem,
+}
+
+impl From<(&str, i64)> for KaraId {
+    fn from((repo, id): (&str, i64)) -> Self {
+        let repo = repo.into();
+        Self { repo, id }
+    }
+}
+
+impl From<(SmallString, i64)> for KaraId {
+    fn from((repo, id): (SmallString, i64)) -> Self {
+        Self { repo, id }
+    }
+}
+
+impl<'a> TaggedObject<'a> for KaraInfo {
+    type TK = <SimpleTagSystem as TaggedObject<'a>>::TK;
+    type TV = <SimpleTagSystem as TaggedObject<'a>>::TV;
+    type TKIter = <SimpleTagSystem as TaggedObject<'a>>::TKIter;
+    type TVIter = <SimpleTagSystem as TaggedObject<'a>>::TVIter;
+
+    fn add_tag(&mut self, tag: Self::TK, value: Option<Self::TV>) -> bool {
+        self.tags.add_tag(tag, value)
+    }
+
+    fn remove_tag<S1: AsRef<str>, S2: AsRef<str>>(&mut self, tag: S1, value: S2) -> bool {
+        self.tags.remove_tag(tag, value)
+    }
+
+    fn drop_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
+        self.tags.drop_tag(tag)
+    }
+
+    fn empty_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
+        self.tags.empty_tag(tag)
+    }
+
+    fn clear_tags(&mut self) -> bool {
+        self.tags.clear_tags()
+    }
+
+    fn tags<'b>(&'b self) -> Self::TKIter
+    where
+        'b: 'a,
+    {
+        self.tags.tags()
+    }
+
+    fn iter_tag<'b, S: AsRef<str>>(&'b self, tag: S) -> Option<Self::TVIter>
+    where
+        'b: 'a,
+    {
+        self.tags.iter_tag(tag)
+    }
+}
+
+/// Represent what we want to override in a kara.
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub enum KaraInfoOverride {
+    SongTitle(SmallString),
+    SongSource(SmallString),
+    SongType(SongType),
+    SongOrigin(SongOrigin),
+
+    AddLanguage(SmallString),
+    RemoveLanguage(SmallString),
+
+    AddKaraMaker(SmallString),
+    RemoveKaraMaker(SmallString),
+}
+
+impl KaraInfo {
+    /// Patch the kara with a list of actions to do in the passed order. If the
+    /// kara information where patched, return true, otherwise return false.
+    pub fn patch_infos<I>(&mut self, overrides: I) -> bool
+    where
+        I: IntoIterator<Item = KaraInfoOverride>,
+    {
+        use KaraInfoOverride::*;
+        macro_rules! set_if_ne {
+            ($what: ident, $str: expr) => {
+                self.$what.ne(&$str).then(|| self.$what = $str).is_some()
+            };
+        }
+        overrides
+            .into_iter()
+            .map(|patch| match patch {
+                SongTitle(title) => set_if_ne!(song_title, title),
+                SongSource(source) => set_if_ne!(song_source, source),
+                SongType(ty) => set_if_ne!(song_type, ty),
+                SongOrigin(origin) => set_if_ne!(song_origin, origin),
+
+                AddLanguage(lang) => self.languages.insert(lang),
+                RemoveLanguage(lang) => self.languages.remove(&lang),
+
+                AddKaraMaker(maker) => self.kara_makers.insert(maker),
+                RemoveKaraMaker(maker) => self.kara_makers.remove(&maker),
+            })
+            .count()
+            != 0
+    }
+}
diff --git a/src/rust/amalib/src/db_objects.rs b/src/rust/amalib/src/db_objects.rs
deleted file mode 100644
index 5ec8f058..00000000
--- a/src/rust/amalib/src/db_objects.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-//! Collection of structures to describe objects retrieved from the lektord
-//! server.
-
-use crate::*;
-
-/// Represent a global id for a kara, the pair (database, id).
-#[derive(Debug, Deserialize, Serialize, Getters, CopyGetters)]
-pub struct KaraId {
-    #[getset(get = "pub")]
-    repo: SmallString,
-
-    #[getset(get_copy = "pub")]
-    id: i64,
-}
-
-/// Represent what we want to override in a kara.
-#[derive(Debug, Deserialize, Serialize)]
-pub struct KaraOverride {}
-
-/// A list of informations about a kara. This is the data from lektor. When
-/// queried we patch it with the overrides given by the user.
-#[derive(Debug, Getters)]
-#[getset(get = "pub")]
-pub struct KaraInfo {
-    song_title: SmallString,
-    song_source: SmallString,
-    song_type: SmallString,
-    song_origin: SmallString,
-    languages: Vec<SmallString>,
-    kara_makers: Vec<SmallString>,
-
-    #[getset(skip)]
-    tags: SimpleTagSystem,
-}
-
-#[allow(dead_code)]
-#[derive(Debug, Getters)]
-#[getset(get = "pub")]
-pub struct Playlist {
-    name: SmallString,
-    ctime: i64,
-    creator: SmallString,
-    content: Vec<i64>,
-
-    #[getset(skip)]
-    tags: SimpleTagSystem,
-}
-
-impl From<(&str, i64)> for KaraId {
-    fn from((repo, id): (&str, i64)) -> Self {
-        let repo = repo.into();
-        Self { repo, id }
-    }
-}
-
-impl From<(SmallString, i64)> for KaraId {
-    fn from((repo, id): (SmallString, i64)) -> Self {
-        Self { repo, id }
-    }
-}
-
-impl<'a> TaggedObject<'a> for Playlist {
-    type TK = <SimpleTagSystem as TaggedObject<'a>>::TK;
-    type TV = <SimpleTagSystem as TaggedObject<'a>>::TV;
-    type TKIter = <SimpleTagSystem as TaggedObject<'a>>::TKIter;
-    type TVIter = <SimpleTagSystem as TaggedObject<'a>>::TVIter;
-
-    fn add_tag(&mut self, tag: Self::TK, value: Option<Self::TV>) -> bool {
-        self.tags.add_tag(tag, value)
-    }
-
-    fn remove_tag<S1: AsRef<str>, S2: AsRef<str>>(&mut self, tag: S1, value: S2) -> bool {
-        self.tags.remove_tag(tag, value)
-    }
-
-    fn drop_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
-        self.tags.drop_tag(tag)
-    }
-
-    fn empty_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
-        self.tags.empty_tag(tag)
-    }
-
-    fn clear_tags(&mut self) -> bool {
-        self.tags.clear_tags()
-    }
-
-    fn tags<'b>(&'b self) -> Self::TKIter
-    where
-        'b: 'a,
-    {
-        self.tags.tags()
-    }
-
-    fn iter_tag<'b, S: AsRef<str>>(&'b self, tag: S) -> Option<Self::TVIter>
-    where
-        'b: 'a,
-    {
-        self.tags.iter_tag(tag)
-    }
-}
-
-impl<'a> TaggedObject<'a> for KaraInfo {
-    type TK = <SimpleTagSystem as TaggedObject<'a>>::TK;
-    type TV = <SimpleTagSystem as TaggedObject<'a>>::TV;
-    type TKIter = <SimpleTagSystem as TaggedObject<'a>>::TKIter;
-    type TVIter = <SimpleTagSystem as TaggedObject<'a>>::TVIter;
-
-    fn add_tag(&mut self, tag: Self::TK, value: Option<Self::TV>) -> bool {
-        self.tags.add_tag(tag, value)
-    }
-
-    fn remove_tag<S1: AsRef<str>, S2: AsRef<str>>(&mut self, tag: S1, value: S2) -> bool {
-        self.tags.remove_tag(tag, value)
-    }
-
-    fn drop_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
-        self.tags.drop_tag(tag)
-    }
-
-    fn empty_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
-        self.tags.empty_tag(tag)
-    }
-
-    fn clear_tags(&mut self) -> bool {
-        self.tags.clear_tags()
-    }
-
-    fn tags<'b>(&'b self) -> Self::TKIter
-    where
-        'b: 'a,
-    {
-        self.tags.tags()
-    }
-
-    fn iter_tag<'b, S: AsRef<str>>(&'b self, tag: S) -> Option<Self::TVIter>
-    where
-        'b: 'a,
-    {
-        self.tags.iter_tag(tag)
-    }
-}
diff --git a/src/rust/amalib/src/db_playlist.rs b/src/rust/amalib/src/db_playlist.rs
new file mode 100644
index 00000000..c50d6fcc
--- /dev/null
+++ b/src/rust/amalib/src/db_playlist.rs
@@ -0,0 +1,56 @@
+//! Describes playlists.
+
+use crate::*;
+
+#[derive(Debug, Getters)]
+#[getset(get = "pub")]
+pub struct Playlist {
+    name: SmallString,
+    ctime: i64,
+    creator: SmallString,
+    content: Vec<i64>,
+
+    #[getset(skip)]
+    tags: SimpleTagSystem,
+}
+
+impl<'a> TaggedObject<'a> for Playlist {
+    type TK = <SimpleTagSystem as TaggedObject<'a>>::TK;
+    type TV = <SimpleTagSystem as TaggedObject<'a>>::TV;
+    type TKIter = <SimpleTagSystem as TaggedObject<'a>>::TKIter;
+    type TVIter = <SimpleTagSystem as TaggedObject<'a>>::TVIter;
+
+    fn add_tag(&mut self, tag: Self::TK, value: Option<Self::TV>) -> bool {
+        self.tags.add_tag(tag, value)
+    }
+
+    fn remove_tag<S1: AsRef<str>, S2: AsRef<str>>(&mut self, tag: S1, value: S2) -> bool {
+        self.tags.remove_tag(tag, value)
+    }
+
+    fn drop_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
+        self.tags.drop_tag(tag)
+    }
+
+    fn empty_tag<S: AsRef<str>>(&mut self, tag: S) -> bool {
+        self.tags.empty_tag(tag)
+    }
+
+    fn clear_tags(&mut self) -> bool {
+        self.tags.clear_tags()
+    }
+
+    fn tags<'b>(&'b self) -> Self::TKIter
+    where
+        'b: 'a,
+    {
+        self.tags.tags()
+    }
+
+    fn iter_tag<'b, S: AsRef<str>>(&'b self, tag: S) -> Option<Self::TVIter>
+    where
+        'b: 'a,
+    {
+        self.tags.iter_tag(tag)
+    }
+}
diff --git a/src/rust/amalib/src/db_tags.rs b/src/rust/amalib/src/db_tags.rs
index 0433d4c9..c04e6d47 100644
--- a/src/rust/amalib/src/db_tags.rs
+++ b/src/rust/amalib/src/db_tags.rs
@@ -66,7 +66,7 @@ pub trait TaggedObject<'a> {
 
     /// Patch the tagged object with a list of actions to do in the passed
     /// order. If the object was patched, return true, otherwise return false.
-    fn patch<'b, I>(&'b mut self, overrides: I) -> bool
+    fn patch_tags<'b, I>(&'b mut self, overrides: I) -> bool
     where
         I: IntoIterator<Item = TagOverride<Self::TK, Self::TV>>,
         'a: 'b,
diff --git a/src/rust/amalib/src/lib.rs b/src/rust/amalib/src/lib.rs
index d02b0fd3..22338b5c 100644
--- a/src/rust/amalib/src/lib.rs
+++ b/src/rust/amalib/src/lib.rs
@@ -7,7 +7,8 @@
 mod connexion;
 mod constants;
 mod db_cache;
-mod db_objects;
+mod db_kara;
+mod db_playlist;
 mod db_tags;
 mod query;
 mod response;
@@ -15,7 +16,8 @@ mod uri;
 
 pub use connexion::*;
 pub use db_cache::*;
-pub use db_objects::*;
+pub use db_kara::*;
+pub use db_playlist::*;
 pub use db_tags::*;
 pub use query::*;
 pub use response::*;
-- 
GitLab