diff --git a/src/rust/liblektor-rs/Cargo.toml b/src/rust/liblektor-rs/Cargo.toml index 16435bdb492da2aa3ebe401c2a36ab4ecd016dda..57f84f400f13a38f1a349e6a3cb72792286a7d76 100644 --- a/src/rust/liblektor-rs/Cargo.toml +++ b/src/rust/liblektor-rs/Cargo.toml @@ -10,4 +10,5 @@ crate-type = [ "staticlib" ] log = "0.4" libc = "0.2.0" diesel_migrations = "2" -diesel = { version = "2", default-features = false, features = [ "sqlite" ] } +diesel = { version = "2", default-features = false, features = [ "sqlite" ] } +serde = { version = "^1", default-features = false, features = [ "std", "derive" ] } diff --git a/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql b/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql index 6abf0cac6c52a22ee8a6e528187178541e5ec60f..7271ce36976b98ba140759733029962e8b2b22ee 100644 --- a/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql +++ b/src/rust/liblektor-rs/migrations/2022-09-30-204512_initial/up.sql @@ -24,7 +24,7 @@ CREATE TABLE kara , song_origin TEXT NOT NULL , source_name TEXT NOT NULL , language TEXT NOT NULL REFERENCES iso_639_1(code) - , kara_hash TEXT NOT NULL -- TEXT ABOVE + HASH OF FILE IN FS + , file_hash TEXT NOT NULL ); -- We can have multiple kara makers for one kara. diff --git a/src/rust/liblektor-rs/src/database/mod.rs b/src/rust/liblektor-rs/src/database/mod.rs index ec02d326f6d0ff0d31bad6984f4081d8f535a3f0..9b910e96103b7ab917dc03508637e7864351fbe7 100644 --- a/src/rust/liblektor-rs/src/database/mod.rs +++ b/src/rust/liblektor-rs/src/database/mod.rs @@ -8,6 +8,10 @@ pub mod models; pub mod schema; pub mod unsafe_interface; +use self::models::*; + +use crate::{database::schema::kara_tags, kurisu_api::v1 as api_v1}; + /// The migrations! const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); @@ -25,3 +29,68 @@ pub fn establish_connection(path: impl AsRef<str>) -> Result<SqliteConnection, S self::run_migration(&mut conn)?; Ok(conn) } + +/// All the information needed to add a kara recieved from a repo! +pub type NewKaraRequest<'a> = ( + models::NewKaraId, + models::NewKara<'a>, + models::NewLanguage<'a>, + Vec<models::AddKaraMaker<'a>>, + Vec<models::AddKaraTag>, +); + +pub struct LktDatabaseConnection { + sqlite: SqliteConnection, +} + +impl LktDatabaseConnection { + /// Get a tag id by its name. + pub fn get_tag_id_by_name(&mut self, tag_name: impl AsRef<str>) -> i32 { + use self::schema::tag::dsl::*; + tag.filter(name.is(tag_name.as_ref())) + .first::<Tag>(&mut self.sqlite) + .unwrap() + .id + } + + /// Get a free local id for all karas. + pub fn get_kara_new_local_id(&mut self) -> i32 { + use self::schema::kara::dsl::*; + use diesel::dsl::*; + kara.select(max(id)) + .first::<Option<i32>>(&mut self.sqlite) + .unwrap() + .unwrap_or(0) + } + + /// Create a series of models from a kara signature from Kurisu's V1 API. + pub fn new_kara<'a>(&mut self, repo_id: u64, kara: api_v1::Kara<'a>) -> NewKaraRequest<'a> { + error!("todo: query the database for a new local id"); + let local_id = self.get_kara_new_local_id(); + let id = NewKaraId { + repo_id: repo_id as i32, + local_kara_id: local_id as i32, + repo_kara_id: kara.id as i32, + }; + let lang = NewLanguage::from(kara.get_language()); + let kara_makers = vec![AddKaraMaker { + id: local_id as i32, + name: kara.author_name, + }]; + let tags = vec![AddKaraTag { + kara_id: local_id as i32, + tag_id: self.get_tag_id_by_name("number"), + value: Some(format!("{}", kara.song_number)), + }]; + let kara = NewKara { + id: local_id as i32, + song_title: kara.song_name, + song_type: kara.song_type, + song_origin: kara.category, + source_name: kara.source_name, + language: lang.code, + file_hash: format!("{}", kara.unix_timestamp), + }; + (id, kara, lang, kara_makers, tags) + } +} diff --git a/src/rust/liblektor-rs/src/database/models.rs b/src/rust/liblektor-rs/src/database/models.rs index a56d60921d0e7d5a3c4324ab989a603bd370e4d2..63914a8daefb3807def36e2997d147d9c8dfb004 100644 --- a/src/rust/liblektor-rs/src/database/models.rs +++ b/src/rust/liblektor-rs/src/database/models.rs @@ -1,13 +1,78 @@ //! Models used for querying, inserting or updating the database. -use crate::database::{schema::*, *}; +use crate::{ + database::{schema::*, *}, + kurisu_api::v1 as api_v1, +}; + +// First the insertable things + +#[derive(Insertable)] +#[diesel(table_name = repo)] +pub struct NewRepo<'a> { + pub id: i32, + pub name: &'a str, +} + +#[derive(Insertable)] +#[diesel(table_name = repo_kara)] +pub struct NewKaraId { + pub repo_id: i32, + pub repo_kara_id: i32, + pub local_kara_id: i32, +} #[derive(Insertable)] #[diesel(table_name = kara)] pub struct NewKara<'a> { + pub id: i32, pub song_title: &'a str, pub song_type: &'a str, pub song_origin: &'a str, pub source_name: &'a str, pub language: &'a str, + pub file_hash: String, +} + +#[derive(Insertable)] +#[diesel(table_name = kara_makers)] +pub struct AddKaraMaker<'a> { + pub id: i32, + pub name: &'a str, +} + +#[derive(Insertable)] +#[diesel(table_name = iso_639_1)] +pub struct NewLanguage<'a> { + pub code: &'a str, + pub name_en: &'a str, + pub is_iso: bool, + pub is_macro: bool, +} + +#[derive(Insertable)] +#[diesel(table_name = kara_tags)] +pub struct AddKaraTag { + pub kara_id: i32, + pub tag_id: i32, + pub value: Option<String>, +} + +impl<'a> From<api_v1::Language<'a>> for NewLanguage<'a> { + fn from(lang: api_v1::Language<'a>) -> Self { + Self { + code: lang.code, + name_en: lang.code, + is_iso: false, + is_macro: false, + } + } +} + +// Then the queriable things +#[derive(Queryable)] +#[diesel(table_name = tag)] +pub struct Tag { + pub id: i32, + pub name: String, } diff --git a/src/rust/liblektor-rs/src/database/schema.rs b/src/rust/liblektor-rs/src/database/schema.rs index 78d438654b092080469df80b1d632119f5a9329b..d45ece40bea531314208616dbd3352cc5b07f7bb 100644 --- a/src/rust/liblektor-rs/src/database/schema.rs +++ b/src/rust/liblektor-rs/src/database/schema.rs @@ -25,7 +25,7 @@ diesel::table! { song_origin -> Text, source_name -> Text, language -> Text, - kara_hash -> Text, + file_hash -> Text, } } diff --git a/src/rust/liblektor-rs/src/database/unsafe_interface.rs b/src/rust/liblektor-rs/src/database/unsafe_interface.rs index 3f79a1fd074bb8f304b356ed2dd31deb8b926ec5..ebd5e1af6c7801fd31b1c69e6eb3c629972fe214 100644 --- a/src/rust/liblektor-rs/src/database/unsafe_interface.rs +++ b/src/rust/liblektor-rs/src/database/unsafe_interface.rs @@ -12,7 +12,7 @@ use std::mem::ManuallyDrop; #[no_mangle] pub unsafe extern "C" fn lkt_database_establish_connection( path: *const u8, -) -> *mut SqliteConnection { +) -> *mut LktDatabaseConnection { let mut path_len = 0; while *path.offset(path_len) != 0 { path_len += 1 @@ -20,7 +20,7 @@ pub unsafe extern "C" fn lkt_database_establish_connection( let len = path_len as usize; let path = ManuallyDrop::new(String::from_raw_parts(path as *mut _, len, len)); match establish_connection(&path[..]) { - Ok(conn) => Box::leak(Box::new(conn)) as *mut _, + Ok(conn) => Box::leak(Box::new(LktDatabaseConnection { sqlite: conn })) as *mut _, Err(err) => { error!("failed to establish connexion to {}: {err}", &path[..]); std::ptr::null_mut() @@ -33,7 +33,7 @@ pub unsafe extern "C" fn lkt_database_establish_connection( /// passed pointer was not obtained by the correct function the behaviour is /// undefined. #[no_mangle] -pub unsafe extern "C" fn lkt_database_close_connection(db: *mut SqliteConnection) { +pub unsafe extern "C" fn lkt_database_close_connection(db: *mut LktDatabaseConnection) { if db == std::ptr::null_mut() { error!("can't clost a connexion to a null database!") } else { diff --git a/src/rust/liblektor-rs/src/kurisu_api/mod.rs b/src/rust/liblektor-rs/src/kurisu_api/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3a6d96c3f59dd67ed1c89948bc4d13c42504661 --- /dev/null +++ b/src/rust/liblektor-rs/src/kurisu_api/mod.rs @@ -0,0 +1 @@ +pub mod v1; diff --git a/src/rust/liblektor-rs/src/kurisu_api/v1.rs b/src/rust/liblektor-rs/src/kurisu_api/v1.rs new file mode 100644 index 0000000000000000000000000000000000000000..438ab11210258755960a0186f55d92a383525e01 --- /dev/null +++ b/src/rust/liblektor-rs/src/kurisu_api/v1.rs @@ -0,0 +1,33 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Kara<'a> { + pub id: u64, + pub source_name: &'a str, + pub song_name: &'a str, + pub song_type: &'a str, + pub song_number: u64, + pub category: &'a str, + pub language: &'a str, + pub author_name: &'a str, + pub author_year: &'a str, + pub is_new: u64, + pub upload_comment: &'a str, + pub popularity: u64, + pub unix_timestamp: u64, + pub size: u64, +} + +#[derive(Debug, Deserialize)] +pub struct Language<'a> { + pub code: &'a str, +} + +impl<'a> Kara<'a> { + /// Get the language out of a kara + pub fn get_language(&self) -> Language<'a> { + Language { + code: self.language, + } + } +} diff --git a/src/rust/liblektor-rs/src/lib.rs b/src/rust/liblektor-rs/src/lib.rs index a40f844be227dcd832ad83239bf1fea3e1f7aba3..7aacf0b761ce85ab72543a84933a3e375b476b15 100644 --- a/src/rust/liblektor-rs/src/lib.rs +++ b/src/rust/liblektor-rs/src/lib.rs @@ -2,6 +2,7 @@ mod compat; mod database; +pub mod kurisu_api; mod module; pub(crate) use compat::*;