diff --git a/src/database.rs b/src/database.rs index 25e8bfc..40b5f86 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,4 +1,7 @@ +use std::i64; + use anyhow::Result; +use ratatui::widgets::block::title; use sqlx::{Row, SqlitePool}; pub struct Database { @@ -7,11 +10,49 @@ pub struct Database { impl Database { pub async fn new() -> Result { - let pool = SqlitePool::connect("sqlite:ratings.db").await?; + let pool = SqlitePool::connect("sqlite://ratings.db?mode=rwc").await?; Ok(Database { pool }) } - pub async fn add_user(&self, name: &str, number: i64) -> Result { + pub async fn create_tables(&self) -> Result<()> { + let mut conn = self.pool.acquire().await?; + + let _ = sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS interprets ( + id INTEGER PRIMARY KEY, + interpret TEXT NOT NULL, + UNIQUE(interpret) + ); + CREATE TABLE IF NOT EXISTS songs ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + interpret_id INTEGER NOT NULL, + FOREIGN KEY("interpret_id") REFERENCES interpret (id), + UNIQUE(title, interpret_id) + ); + CREATE TABLE IF NOT EXISTS ratings ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + song_id INTEGER NOT NULL, + rating INTEGER NOT NULL, + FOREIGN KEY("user_id") REFERENCES users (id), + FOREIGN KEY("song_id") REFERENCES songs (id), + UNIQUE(user_id, song_id) + ); + "#, + ) + .execute(&mut *conn) + .await?; + + Ok(()) + } + + pub async fn add_user(&self, user_id: i64, name: &str) -> Result { let mut conn = self.pool.acquire().await?; let id = sqlx::query( @@ -20,8 +61,8 @@ impl Database { VALUES (?1, ?2) "#, ) + .bind(user_id) .bind(name) - .bind(number) .execute(&mut *conn) .await? .last_insert_rowid(); @@ -29,7 +70,7 @@ impl Database { Ok(id) } - pub async fn user_exists(&self, number: i64) -> Result { + pub async fn user_exists(&self, user_id: i64) -> Result { let mut conn = self.pool.acquire().await?; let record = sqlx::query( @@ -38,7 +79,7 @@ impl Database { WHERE id = ? "#, ) - .bind(number) + .bind(user_id) .fetch_one(&mut *conn) .await?; @@ -46,4 +87,78 @@ impl Database { Ok(id) } + + pub async fn user_add_rating( + &self, + user_id: i64, + interpret: &str, + song: &str, + rating: i64, + ) -> Result<()> { + let mut conn = self.pool.acquire().await?; + + // Add interpret + let _ = sqlx::query( + r#" + INSERT OR IGNORE INTO interprets (interpret) + VALUES (?1) + "#, + ) + .bind(interpret) + .execute(&mut *conn) + .await?; + + // Get the interpret ID + let record = sqlx::query( + r#" + SELECT id FROM interprets + WHERE interpret = ?1 + "#, + ) + .bind(interpret) + .fetch_one(&mut *conn) + .await?; + + let interpret_id: i64 = record.try_get("id")?; + + // Add the song + let _ = sqlx::query( + r#" + INSERT OR IGNORE INTO songs (title, interpret_id) + VALUES (?1, ?2) + "#, + ) + .bind(song) + .bind(interpret_id) + .execute(&mut *conn) + .await?; + + // Get the song ID + let record = sqlx::query( + r#" + SELECT id FROM songs + WHERE title = ?1 + "#, + ) + .bind(song) + .fetch_one(&mut *conn) + .await?; + + let song_id: i64 = record.try_get("id")?; + + // Add the rating + let _ = sqlx::query( + r#" + INSERT INTO ratings (user_id, song_id, rating) + VALUES (?1, ?2, ?3) + "#, + ) + .bind(user_id) + .bind(song_id) + .bind(rating) + .execute(&mut *conn) + .await?; + + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index 1f3d750..cf39fe8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,17 @@ use tokio; async fn main() { let player = player::MprisPlayer::new().expect("Could not create player"); - let (usernumber, userrating) = userinterface::get_user_rating().expect("Lala"); + let db = database::Database::new() + .await + .expect("Could not create database"); + + let (usernumber, userrating) = userinterface::get_user_rating(&db).await.expect("Lala"); let track = player .get_interpret_and_track() .expect("Could not read track"); println!("User: {usernumber} with rating: {userrating}: {track}"); + + db.create_tables().await.unwrap(); + //db.add_user(1, "Janek").await.unwrap(); } diff --git a/src/userinterface.rs b/src/userinterface.rs index 2d359b7..cc6145b 100644 --- a/src/userinterface.rs +++ b/src/userinterface.rs @@ -12,19 +12,35 @@ use tui_textarea::{Input, Key, TextArea}; use anyhow::Result; -fn validate_user(textarea: &mut TextArea) -> bool { - if let Err(_) = textarea.lines()[0].parse::() { - textarea.set_style(Style::default().fg(Color::LightRed)); - textarea.set_block( - Block::default() - .borders(Borders::ALL) - .title(format!("ERROR: Please enter a valid user number")), - ); - false - } else { - textarea.set_style(Style::default().fg(Color::LightGreen)); - textarea.set_block(Block::default().borders(Borders::ALL).title("OK")); - true +use crate::database::Database; + +async fn validate_user(textarea: &mut TextArea<'_>, db: &Database) -> bool { + match textarea.lines()[0].parse::() { + Err(_) => { + textarea.set_style(Style::default().fg(Color::LightRed)); + textarea.set_block( + Block::default() + .borders(Borders::ALL) + .title(format!("ERROR: Please enter a valid user number")), + ); + false + } + Ok(value) => { + let db_user = db.user_exists(value as i64).await.is_ok(); + if db_user { + textarea.set_style(Style::default().fg(Color::LightGreen)); + textarea.set_block(Block::default().borders(Borders::ALL).title("OK")); + true + } else { + textarea.set_style(Style::default().fg(Color::LightRed)); + textarea.set_block( + Block::default() + .borders(Borders::ALL) + .title(format!("ERROR: Please enter a valid user number")), + ); + false + } + } } } @@ -56,7 +72,7 @@ fn validate_rating(textarea: &mut TextArea) -> bool { } } -pub fn get_user_rating() -> Result<(String, String)> { +pub async fn get_user_rating(db: &Database) -> Result<(String, String)> { let stdout = io::stdout(); let mut stdout = stdout.lock(); @@ -70,7 +86,7 @@ pub fn get_user_rating() -> Result<(String, String)> { textarea.set_placeholder_text("USER: Enter a valid user number (e.g. 156)"); let layout = Layout::default().constraints([Constraint::Length(3), Constraint::Min(1)].as_slice()); - let mut is_valid = validate_user(&mut textarea); + let mut is_valid = validate_user(&mut textarea, db).await; loop { term.draw(|f| { @@ -95,7 +111,7 @@ pub fn get_user_rating() -> Result<(String, String)> { input => { // TextArea::input returns if the input modified its text if textarea.input(input) { - is_valid = validate_user(&mut textarea); + is_valid = validate_user(&mut textarea, db).await; } } }