From 99dc27da1650a6fd45ac7fce6eeba8d65aa3ed37 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Thu, 5 Jan 2023 21:54:26 +0100
Subject: [PATCH] LIBLEKTOR-RS: DB now supports multiple languages per kara

---
 .../2022-09-30-204512_initial/down.sql        |  3 +-
 .../2022-09-30-204512_initial/up.sql          |  7 ++++-
 .../liblektor-rs/lektor_db/src/connexion.rs   | 28 ++++++++++++-------
 src/rust/liblektor-rs/lektor_db/src/lib.rs    |  2 +-
 src/rust/liblektor-rs/lektor_db/src/models.rs |  8 +++++-
 src/rust/liblektor-rs/lektor_db/src/schema.rs | 12 ++++++--
 6 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/down.sql b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/down.sql
index cee1b6a5..b24c8023 100644
--- a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/down.sql
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/down.sql
@@ -6,4 +6,5 @@ DROP TABLE kara_tag;
 DROP TABLE tag;
 DROP TABLE kara_tags;
 DROP TABLE history;
-DROP TABLE iso_639_1;
\ No newline at end of file
+DROP TABLE iso_639_1;
+DROP TABLE kara_langs;
\ No newline at end of file
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
index dd9b2691..dcd18261 100644
--- a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
@@ -23,7 +23,6 @@ CREATE TABLE kara
   , song_type   TEXT    NOT NULL
   , song_origin TEXT    NOT NULL
   , source_name TEXT    NOT NULL
-  , language    TEXT    NOT NULL REFERENCES iso_639_1(code)
   , file_hash   TEXT    NOT NULL UNIQUE
   );
 
@@ -65,6 +64,12 @@ CREATE TABLE iso_639_1
   , is_macro BOOLEAN NOT NULL DEFAULT false
   );
 
+CREATE TABLE kara_langs
+  ( id   INTEGER NOT NULL REFERENCES kara      ON DELETE CASCADE
+  , code TEXT    NOT NULL REFERENCES iso_639_1 ON DELETE CASCADE
+  , PRIMARY KEY (id, code)
+  );
+
 -- As defined in ISO 639-1:
 -- https://archive.wikiwix.com/cache/index2.php?url=http%3A%2F%2Fwww.sil.org%2Fiso639-3%2Fcodes.asp%3Forder%3D639_1%26letter%3D%2525#federation=archive.wikiwix.com&tab=url
 INSERT OR REPLACE INTO iso_639_1 (is_macro, is_iso, code, name_en) VALUES
diff --git a/src/rust/liblektor-rs/lektor_db/src/connexion.rs b/src/rust/liblektor-rs/lektor_db/src/connexion.rs
index 68047761..75907ae3 100644
--- a/src/rust/liblektor-rs/lektor_db/src/connexion.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/connexion.rs
@@ -98,24 +98,33 @@ impl LktDatabaseConnection {
 
     /// Ensure that a given language is present in the database. If it's not
     /// insert it. Existence test is done on the code of the language.
-    pub fn ensure_language_exists(&mut self, lang: &Language) -> LktDatabaseResult<()> {
+    pub fn ensure_languages_exist<'a>(&mut self, langs: &[Language<'a>]) -> LktDatabaseResult<()> {
         self.sqlite.exclusive_transaction(|c| {
-            with_dsl!(iso_639_1 => match iso_639_1.filter(code.eq(lang.code)).count().get_result(c)? {
-                1 => Ok(()),
-                0 => { diesel::insert_into(iso_639_1).values(lang).execute(c)?; Ok(()) }
-                count => Err(LktDatabaseError::String(format!("language `{lang:?}` has {count} occurences in the database..."))),
-            })
+            for lang in langs {
+                with_dsl!(iso_639_1 => match iso_639_1.filter(code.eq(lang.code)).count().get_result(c)? {
+                    1 => {},
+                    0 => { diesel::insert_into(iso_639_1).values(langs).execute(c)?; }
+                    count => return Err(LktDatabaseError::String(format!("language `{lang:?}` has {count} occurences in the database..."))),
+                })
+            }
+            Ok(())
         })
     }
 
     /// Add a kara with a request.
     pub fn add_kara_from_request(&mut self, kara: NewKaraRequest) -> LktDatabaseResult<()> {
-        let (id, new_kara, lang, karamakers, tags) = kara;
-        self.ensure_language_exists(&lang)?;
+        let (id, new_kara, karamakers, langs, tags) = kara;
+        self.ensure_languages_exist(&langs)?;
         self.sqlite.exclusive_transaction(|c| {
             with_dsl!(kara        => diesel::insert_into(kara).values(&new_kara).execute(c)?);
             with_dsl!(repo_kara   => diesel::insert_into(repo_kara).values(id).execute(c)?);
             with_dsl!(kara_makers => diesel::insert_or_ignore_into(kara_makers).values(karamakers).execute(c)?);
+            with_dsl!(kara_langs  => {
+                use models::{Language, KaraLanguage};
+                let langs = langs.into_iter().map(|Language { code: lang, .. }| KaraLanguage { id: new_kara.id, code: lang } );
+                diesel::delete(kara_langs.filter(id.eq(new_kara.id))).execute(c)?;
+                diesel::insert_into(kara_langs).values(langs.collect::<Vec<_>>()).execute(c)?;
+            });
             with_dsl!(kara_tags   => {
                 diesel::delete(kara_tags.filter(kara_id.eq(new_kara.id))).execute(c)?;
                 diesel::insert_into(kara_tags).values(tags).execute(c)?;
@@ -153,10 +162,9 @@ impl LktDatabaseConnection {
             song_type: kara.song_type,
             song_origin: kara.category,
             source_name: kara.source_name,
-            language: lang.code,
             file_hash: format!("{}", kara.unix_timestamp),
         };
-        Ok((id, kara, lang, kara_makers, tags))
+        Ok((id, kara, kara_makers, vec![lang], tags))
     }
 
     /// Peek the next kara to play
diff --git a/src/rust/liblektor-rs/lektor_db/src/lib.rs b/src/rust/liblektor-rs/lektor_db/src/lib.rs
index 8f8e39f1..4043328d 100644
--- a/src/rust/liblektor-rs/lektor_db/src/lib.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/lib.rs
@@ -14,7 +14,7 @@ pub(self) use std::{collections::VecDeque, ops::Range, path::Path};
 pub type NewKaraRequest<'a> = (
     models::KaraId,
     models::NewKara<'a>,
-    models::Language<'a>,
     Vec<models::KaraMaker<'a>>,
+    Vec<models::Language<'a>>,
     Vec<models::AddKaraTag>,
 );
diff --git a/src/rust/liblektor-rs/lektor_db/src/models.rs b/src/rust/liblektor-rs/lektor_db/src/models.rs
index c8c2b21c..60940e02 100644
--- a/src/rust/liblektor-rs/lektor_db/src/models.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/models.rs
@@ -28,7 +28,6 @@ pub struct NewKara<'a> {
     pub song_type: &'a str,
     pub song_origin: &'a str,
     pub source_name: &'a str,
-    pub language: &'a str,
     pub file_hash: String,
 }
 
@@ -39,6 +38,13 @@ pub struct KaraMaker<'a> {
     pub name: &'a str,
 }
 
+#[derive(Debug, Insertable, Queryable, Selectable)]
+#[diesel(table_name = kara_langs)]
+pub struct KaraLanguage<'a> {
+    pub id: i32,
+    pub code: &'a str,
+}
+
 #[derive(Debug, Insertable, Queryable, Selectable)]
 #[diesel(table_name = iso_639_1)]
 pub struct Language<'a> {
diff --git a/src/rust/liblektor-rs/lektor_db/src/schema.rs b/src/rust/liblektor-rs/lektor_db/src/schema.rs
index d45ece40..48a94b8b 100644
--- a/src/rust/liblektor-rs/lektor_db/src/schema.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/schema.rs
@@ -24,11 +24,17 @@ diesel::table! {
         song_type -> Text,
         song_origin -> Text,
         source_name -> Text,
-        language -> Text,
         file_hash -> Text,
     }
 }
 
+diesel::table! {
+    kara_langs (id, code) {
+        id -> Integer,
+        code -> Text,
+    }
+}
+
 diesel::table! {
     kara_makers (id, name) {
         id -> Integer,
@@ -67,7 +73,8 @@ diesel::table! {
 }
 
 diesel::joinable!(history -> kara (id));
-diesel::joinable!(kara -> iso_639_1 (language));
+diesel::joinable!(kara_langs -> iso_639_1 (code));
+diesel::joinable!(kara_langs -> kara (id));
 diesel::joinable!(kara_makers -> kara (id));
 diesel::joinable!(kara_tags -> kara (kara_id));
 diesel::joinable!(kara_tags -> tag (tag_id));
@@ -78,6 +85,7 @@ diesel::allow_tables_to_appear_in_same_query!(
     history,
     iso_639_1,
     kara,
+    kara_langs,
     kara_makers,
     kara_tags,
     repo,
-- 
GitLab