From b79192328dac5b06aed2e7802ff76963c4fe465d Mon Sep 17 00:00:00 2001 From: MrDulfin Date: Sun, 2 Jun 2024 17:30:16 -0400 Subject: [PATCH] First Commit --- .gitignore | 1 + Cargo.lock | 72 +++++++++ Cargo.toml | 10 ++ src/error.rs | 13 ++ src/lib.rs | 415 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/traits.rs | 3 + 6 files changed, 514 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/traits.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..dd4b522 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,72 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "kushi" +version = "0.1.0" +dependencies = [ + "thiserror", + "uuid", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6907d9f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "kushi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1.0.61" +uuid = "1.8.0" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..b81f78f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum QueueError { + #[error("Index out of bounds! Index {index} is over len {len}")] + OutOfBounds { index: usize, len: usize }, + #[error("The Queue is empty!")] + EmptyQueue, + #[error("There are no past played songs!")] + EmptyPlayed, + #[error("There is no item after this in the Queue")] + NoNext, +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..23c25f9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,415 @@ +use std::fmt::Debug; + +use error::QueueError; +use traits::{Location, TrackGroup}; + +pub mod error; +pub mod traits; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum QueueState { + Played, + First, + AddHere, + NoState, +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub struct QueueItem< + T: Debug + Clone + PartialEq, // T: The Singular Item Type + U: Debug + PartialEq + Clone + TrackGroup, // U: The Multi-Item Type. Needs to be tracked as multiple items + L: Debug + PartialEq + Clone + Copy + Location // L: The Location Type. Optional but maybe useful +> { + pub(crate) item: QueueItemType, + pub(crate) state: QueueState, + pub(crate) source: Option, + pub(crate) by_human: bool, +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum QueueItemType< + T: Debug + Clone + PartialEq, // T: The Singular Item Type + U: Debug + PartialEq + Clone + TrackGroup, // U: The Multi-Item Type. Needs to be tracked as multiple items +> { + Single(T), + Multi(U) +} + +impl< + T: Debug + Clone + PartialEq, + U: Debug + PartialEq + Clone + TrackGroup, + L: Debug + PartialEq + Clone + Copy + Location +> +QueueItem { + pub fn from_item_type(item: QueueItemType, source: Option) -> Self { + QueueItem { + item: item, + state: QueueState::NoState, + source, + by_human: false, + } + } +} + +#[derive(Debug, Default)] +pub struct Queue< + T: Debug + Clone + PartialEq, // T: The Singular Item Type + U: Debug + PartialEq + Clone + TrackGroup, // U: The Multi-Item Type. Needs to be tracked as multiple items + L: Debug + PartialEq + Clone + Copy + Location // L: The Location Type. Optional but maybe useful +> { + pub items: Vec>, + pub played: Vec>, + pub loop_: bool, + pub shuffle: Option>, +} + +// TODO: HAndle the First QueueState[looping] and shuffle +impl< + T: Debug + Clone + PartialEq, + U: Debug + PartialEq + Clone + TrackGroup, + L: Debug + PartialEq + Clone + Copy + Location +> Queue { + fn has_addhere(&self) -> bool { + for item in &self.items { + if item.state == QueueState::AddHere { + return true; + } + } + false + } + + #[allow(unused)] + pub(crate) fn dbg_items(&self) { + dbg!( + self.items + .iter() + .map(|item| (&item.item, item.state)) + .collect::, QueueState)>>(), + self.items.len() + ); + } + + pub fn set_items(&mut self, tracks: Vec>) { + let mut tracks = tracks; + self.items.clear(); + self.items.append(&mut tracks); + } + + /// Inserts an item after the AddHere item + pub fn add_item(&mut self, item: QueueItemType, source: Option, by_human: bool) { + let mut i: usize = 0; + + self.items = self + .items + .iter() + .enumerate() + .map(|(j, item_)| { + let mut item_ = item_.to_owned(); + // get the index of the current AddHere item and give it to i + if item_.state == QueueState::AddHere { + i = j; + item_.state = QueueState::NoState; + } + item_ + }) + .collect::>>(); + + self.items.insert( + i + if self.items.is_empty() { 0 } else { 1 }, + QueueItem { + item, + state: QueueState::AddHere, + source, + by_human, + }, + ); + } + + /// Inserts an item after the currently playing item + pub fn add_item_next(&mut self, item: QueueItemType, source: Option) { + use QueueState::*; + let empty = self.items.is_empty(); + + self.items.insert( + if empty { 0 } else { 1 }, + QueueItem { + item, + state: if (self.items.get(1).is_none() + || !self.has_addhere() && self.items.get(1).is_some()) + || empty + { + AddHere + } else { + NoState + }, + source, + by_human: true, + }, + ) + } + + pub fn add_multi(&mut self, items: Vec>, source: Option, by_human: bool) { + let mut i: usize = 0; + + self.items = self + .items + .iter() + .enumerate() + .map(|(j, item_)| { + let mut item_ = item_.to_owned(); + // get the index of the current AddHere item and give it to i + if item_.state == QueueState::AddHere { + i = j; + item_.state = QueueState::NoState; + } + item_ + }) + .collect::>>(); + + let empty = self.items.is_empty(); + + let len = items.len(); + for item in items.into_iter().rev() { + self.items.insert( + i + if empty { 0 } else { 1 }, + QueueItem { + item, + state: QueueState::NoState, + source, + by_human, + }, + ); + } + self.items[i + len - if empty { 1 } else { 0 }].state = QueueState::AddHere; + } + + /// Add multiple Items after the currently playing Item + pub fn add_multi_next(&mut self, items: Vec>, source: Option) { + use QueueState::*; + let empty = self.items.is_empty(); + + let add_here = (self.items.get(1).is_none() + || !self.has_addhere() && self.items.get(1).is_some()) + || empty; + + let len = items.len(); + + for item in items { + self.items.insert( + if empty { 0 } else { 1 }, + QueueItem { + item, + state: NoState, + source, + by_human: true, + }, + ) + } + + if add_here { + self.items[len - if empty { 1 } else { 0 }].state = QueueState::AddHere; + } + } + + pub fn remove_item(&mut self, remove_index: usize) -> Result, QueueError> { + // dbg!(/*&remove_index, self.current_index(), &index,*/ &self.items[remove_index]); + + if remove_index < self.items.len() { + // update the state of the next item to replace the item being removed + if self.items.get(remove_index + 1).is_some() { + self.items[remove_index + 1].state = self.items[remove_index].state; + } + Ok(self.items.remove(remove_index)) + } else { + Err(QueueError::EmptyQueue) + } + } + + pub fn insert( + &mut self, + index: usize, + new_item: QueueItemType, + source: Option, + addhere: bool, + ) -> Result<(), QueueError> { + if self.items.get_mut(index).is_none() + && index > 0 + && self.items.get_mut(index - 1).is_none() + { + return Err(QueueError::OutOfBounds { + index, + len: self.items.len(), + }); + } + if addhere { + let mut new_item = QueueItem::from_item_type(new_item, source); + for item in &mut self.items { + if item.state == QueueState::AddHere { + item.state = QueueState::NoState + } + } + new_item.state = QueueState::AddHere; + self.items.insert(index, new_item); + } else { + let new_item = QueueItem::from_item_type(new_item, source); + self.items.insert(index, new_item); + } + Ok(()) + } + + pub fn clear(&mut self) { + self.items.clear(); + } + + pub fn clear_except(&mut self, index: usize) -> Result<(), QueueError> { + use QueueState::*; + let empty = self.items.is_empty(); + + if !empty && index < self.items.len() { + let i = self.items[index].clone(); + self.items.retain(|item| *item == i); + self.items[0].state = AddHere; + } else if empty { + return Err(QueueError::EmptyQueue); + } else { + return Err(QueueError::OutOfBounds { + index, + len: self.items.len(), + }); + } + Ok(()) + } + + pub fn clear_played(&mut self) { + self.played.clear(); + } + + pub fn clear_all(&mut self) { + self.items.clear(); + self.played.clear(); + } + + pub fn move_to(&mut self, index: usize) -> Result<(), QueueError> { + use QueueState::*; + + + + let empty = self.items.is_empty(); + + let index = if !empty { + index + } else { + return Err(QueueError::EmptyQueue); + }; + + if !empty && dbg!(index < self.items.len()) { + let to_item = self.items[index].clone(); + + if let QueueItemType::Multi(_) = to_item.item { + unimplemented!(); //TODO: Add logic for multi items + } + + loop { + let empty = self.items.is_empty(); + let item = self.items[0].item.to_owned(); + + if item != to_item.item && !empty { + if self.items[0].state == AddHere && self.items.get(1).is_some() { + self.items[1].state = AddHere; + } + let item = self.items.remove(0); + self.played.push(item); + + // dbg!(&to_item.item, &self.items[ind].item); + } else if empty { + return Err(QueueError::EmptyQueue); + } else { + break; + } + } + } else { + return Err(QueueError::EmptyQueue); + } + Ok(()) + } + + pub fn swap(&mut self, a: usize, b: usize) { + self.items.swap(a, b) + } + + pub fn move_item(&mut self, from: usize, to: usize) { + let item = self.items[from].to_owned(); + if from != to { + self.items.remove(from); + } + self.items.insert(to, item); + } + + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Result<&QueueItem, QueueError> { + if self.items.is_empty() { + if self.loop_ { + unimplemented!() // TODO: add function to loop the queue + } else { + return Err(QueueError::EmptyQueue); + } + } + + if self.items[0].state == QueueState::AddHere || !self.has_addhere() { + if let QueueItemType::Multi(_) = self.items[0].item { + unimplemented!(); // TODO: Handle Multi items here? + } + + self.items[0].state = QueueState::NoState; + if self.items.get_mut(1).is_some() { + self.items[1].state = QueueState::AddHere; + } + } + let item = self.items.remove(0); + self.played.push(item); + + if self.items.is_empty() { + Err(QueueError::NoNext) + } else { + Ok(&self.items[0]) + } + } + + pub fn prev(&mut self) -> Result<&QueueItem, QueueError> { + if let Some(item) = self.played.pop() { + if item.state == QueueState::First && self.loop_ { + todo!() + } + + if let QueueItemType::Multi(_) = self.items[0].item { + unimplemented!(); // TODO: Handle Multi items here? + } if let QueueItemType::Multi(_) = item.item { + unimplemented!(); // TODO: Handle Multi items here? + } + + self.items.insert(0, item); + Ok(&self.items[0]) + } else { + Err(QueueError::EmptyPlayed) + } + } + + pub fn current(&self) -> Result<&QueueItem, QueueError> { + if !self.items.is_empty() { + if let QueueItemType::Multi(_) = self.items[0].item { + unimplemented!(); // TODO: Handle Multi items here? + } + Ok(&self.items[0]) + } else { + Err(QueueError::EmptyQueue) + } + } + + pub fn check_played(&mut self, limit: usize) { + while self.played.len() > limit { + self.played.remove(0); + } + } +} \ No newline at end of file diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..306fc19 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,3 @@ +pub trait TrackGroup {} + +pub trait Location {} \ No newline at end of file