diff --git a/src/db_operations.rs b/src/db_operations.rs index 2494578..504e830 100644 --- a/src/db_operations.rs +++ b/src/db_operations.rs @@ -1,8 +1,7 @@ use derive_more::From; use rusqlite::{params, Connection, Result}; -use crate::file_operations::ItemTag; -use crate::server_handling::PartialTag; +use crate::message_types::{ItemTag, PartialTag}; /// Catch all Error for database creation errors #[derive(From, Debug)] @@ -16,6 +15,22 @@ pub struct DatabaseRequest { pub search_tag: PartialTag, } +impl SearchType { + pub fn is_where(self: &Self) -> bool { + match self { + SearchType::Where => true, + SearchType::Like => false, + } + } + + pub fn is_like(self: &Self) -> bool { + match self { + SearchType::Where => false, + SearchType::Like => true, + } + } +} + pub enum SearchType { Where, Like, @@ -114,38 +129,54 @@ impl DBObject { let mut conditions = Vec::::new(); + let does_have_wild = if request.search_type.is_like() { + ("'%", "%'") + } else { + ("'", "'") + }; + if request.search_tag.has_path() { conditions.push(format!( - "path = '{}'", - request.search_tag.path.clone().unwrap() + "path = {}{}{}", + does_have_wild.0, + request.search_tag.path.clone().unwrap(), + does_have_wild.1, )); } if request.search_tag.has_title() { conditions.push(format!( - "title = '{}'", - request.search_tag.title.clone().unwrap() + "title = {}{}{}", + does_have_wild.0, + request.search_tag.title.clone().unwrap(), + does_have_wild.1, )); } if request.search_tag.has_artist() { conditions.push(format!( - "artist = '{}'", - request.search_tag.artist.clone().unwrap() + "artist = {}{}{}", + does_have_wild.0, + request.search_tag.artist.clone().unwrap(), + does_have_wild.1, )); } if request.search_tag.has_album() { conditions.push(format!( - "album = '{}'", - request.search_tag.album.clone().unwrap() + "album = {}{}{}", + does_have_wild.0, + request.search_tag.album.clone().unwrap(), + does_have_wild.1, )); } if request.search_tag.has_album_artist() { conditions.push(format!( - "album_artist = '{}'", - request.search_tag.album_artist.clone().unwrap() + "album_artist = {}{}{}", + does_have_wild.0, + request.search_tag.album_artist.clone().unwrap(), + does_have_wild.1, )); } diff --git a/src/file_operations.rs b/src/file_operations.rs index c1aca90..443cad5 100644 --- a/src/file_operations.rs +++ b/src/file_operations.rs @@ -1,8 +1,9 @@ use id3::{Tag, TagLike}; use std::path::PathBuf; - use scan_dir::ScanDir; +use crate::message_types::{ItemTag}; + const SUPPORTED_FILETYPES: [&str; 1] = ["mp3"]; /// The object that iteratively and recursively scans the directories @@ -77,27 +78,6 @@ impl Iterator for MusicScanner { } } -/// A struct that defines all the music tags supported by Sousa -#[derive(Debug, Clone)] -pub struct ItemTag { - pub path: String, - pub title: String, - pub artist: String, - pub album: String, - pub album_artist: String, -} - -impl ItemTag { - pub fn new() -> Self { - ItemTag { - path: String::new(), - title: String::new(), - artist: String::new(), - album: String::new(), - album_artist: String::new(), - } - } -} /// Returns the music information from a filepath pub fn get_tag(filepath: &PathBuf) -> Result { diff --git a/src/main.rs b/src/main.rs index 53a659d..70418d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use rodio::{Decoder, OutputStream, Sink}; -use server_handling::UIRequest; use std::io::BufReader; use std::net::TcpStream; use std::path::PathBuf; @@ -11,11 +10,13 @@ use clap::Parser; use dirs_next; use crate::db_operations::DatabaseRequest; -use crate::server_handling::PartialTag; - pub mod db_operations; pub mod file_operations; pub mod server_handling; +pub mod message_types; + +use crate::message_types::{PartialTag, ServerResponse, UIRequest}; + #[derive(Parser, Debug)] #[command(author, version, about, long_about=None)] @@ -74,7 +75,7 @@ fn main() { } let test_tag = PartialTag { - title: Some("%bees%".to_string()), + title: Some("bees".to_string()), ..PartialTag::default() }; @@ -124,7 +125,32 @@ fn main() { UIRequest::Play => sink.play(), UIRequest::Pause => sink.pause(), UIRequest::Skip(skip_direction) => todo!(), - UIRequest::GetList(request) => todo!(), + UIRequest::Search(request) => { + println!("got a: {:?}", request); + let items = dbo + .get(&DatabaseRequest { + search_type: db_operations::SearchType::Like, + search_tag: request, + }) + .unwrap(); + + match items { + None => sockets[i].write_message("None".into()).unwrap(), + Some(items) => { + sockets[i] + .write_message( + serde_json::to_string(&ServerResponse { + search_results: items, + }) + .unwrap() + .into(), + ) + .unwrap(); + } + } + + //println!("got from db: {:?}", items); + } UIRequest::SwitchTo(partial_tag) => todo!(), UIRequest::GetStatus => todo!(), }, diff --git a/src/message_types.rs b/src/message_types.rs new file mode 100644 index 0000000..d68cb8c --- /dev/null +++ b/src/message_types.rs @@ -0,0 +1,95 @@ +use serde::{Deserialize, Serialize}; + +/// A struct that defines all the music tags supported by Sousa +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ItemTag { + pub path: String, + pub title: String, + pub artist: String, + pub album: String, + pub album_artist: String, +} + +impl Default for ItemTag { + fn default() -> Self { + ItemTag { + path: String::new(), + title: String::new(), + artist: String::new(), + album: String::new(), + album_artist: String::new(), + } + } +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct PartialTag { + pub path: Option, + pub title: Option, + pub artist: Option, + pub album: Option, + pub album_artist: Option, +} + +impl Default for PartialTag { + fn default() -> Self { + PartialTag { + path: None, + title: None, + artist: None, + album: None, + album_artist: None, + } + } +} + +impl PartialTag { + pub fn has_path(self: &Self) -> bool { + self.path.is_some() + } + + pub fn has_title(self: &Self) -> bool { + self.title.is_some() + } + + pub fn has_artist(self: &Self) -> bool { + self.artist.is_some() + } + + pub fn has_album(self: &Self) -> bool { + self.album.is_some() + } + + pub fn has_album_artist(self: &Self) -> bool { + self.album_artist.is_some() + } + + pub fn is_empty(self: &Self) -> bool { + return self.path.is_none() + && self.title.is_none() + && self.artist.is_none() + && self.album.is_none() + && self.album_artist.is_none(); + } +} + +#[derive(Serialize, Deserialize)] +pub struct ServerResponse { + pub search_results: Vec, +} + +#[derive(Serialize, Deserialize)] +pub enum SkipDirection { + Forward, + Backward, +} + +#[derive(Serialize, Deserialize)] +pub enum UIRequest { + Play, + Pause, + Skip(SkipDirection), + Search(PartialTag), + SwitchTo(PartialTag), + GetStatus, +} diff --git a/src/server_handling.rs b/src/server_handling.rs index 9a0074b..9157ed2 100644 --- a/src/server_handling.rs +++ b/src/server_handling.rs @@ -1,74 +1,17 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize)] -pub struct PartialTag { - pub path: Option, - pub title: Option, - pub artist: Option, - pub album: Option, - pub album_artist: Option, -} - -impl Default for PartialTag { - fn default() -> Self { - PartialTag { - path: None, - title: None, - artist: None, - album: None, - album_artist: None, - } - } -} - -impl PartialTag { - pub fn has_path(self: &Self) -> bool { - self.path.is_some() - } - - pub fn has_title(self: &Self) -> bool { - self.title.is_some() - } - - pub fn has_artist(self: &Self) -> bool { - self.artist.is_some() - } - - pub fn has_album(self: &Self) -> bool { - self.album.is_some() - } - - pub fn has_album_artist(self: &Self) -> bool { - self.album_artist.is_some() - } - - pub fn is_empty(self: &Self) -> bool { - return self.path.is_none() - && self.title.is_none() - && self.artist.is_none() - && self.album.is_none() - && self.album_artist.is_none(); - } -} - -#[derive(Serialize, Deserialize)] -pub enum SkipDirection { - Forward, - Backward, -} - -#[derive(Serialize, Deserialize)] -pub enum UIRequest { - Play, - Pause, - Skip(SkipDirection), - GetList(String), - SwitchTo(PartialTag), - GetStatus, -} +use crate::message_types::{UIRequest}; +/// Pass a pub fn handle_request(socket_message: String) -> Result { println!("Recieved a socket message: {}", socket_message); let request: UIRequest = serde_json::from_str(&socket_message)?; + Ok(request) } + +fn sanitize_input(input: UIRequest) -> Result { + // if UIRequest is a search string, make sure it is + // not empty + // has no %, this is a fuzzy search, This program handles that, maybe replace * with % + // has a type of request (e.g. "title search: value") + Ok(UIRequest::Pause) +}