ngtask_queue_basic 0.1.0

A trait-based abstraction layer for task queues with ID and category support
Documentation
use std::{collections::HashMap, sync::{atomic::{AtomicUsize, Ordering}, Arc, Mutex}};
use ngtq::{ NGTQError, NGTQ };

static IDCOUNTER: AtomicUsize = AtomicUsize::new(0);
pub struct TaskQueue {
    pub is_initialised: bool,
    pub id_queue: HashMap<String, String>,
    pub category_queues: HashMap<String, Vec<String>>,
}

impl TaskQueue {
    fn initialised_check(&self, issue_desc: &str) -> Result<(), NGTQError> {
        if !self.is_initialised {
            return Err(
                NGTQError::generate_error(
                    ngtq::NGTQErrorType::Initialisation(String::from("The object was not initialised")), 
                    String::from(issue_desc)
                ))
        }
        Ok(())
    }
}


impl NGTQ for TaskQueue {
    fn initialise() -> Arc<Mutex<TaskQueue>> {
        let is_initialised = true;
        let id_queue: HashMap<String, String> = HashMap::new();
        let category_queues: HashMap<String, Vec<String>> = HashMap::new();

        Arc::new(Mutex::new(TaskQueue { is_initialised, id_queue, category_queues }))
    }

    fn get_id_queue_len(&self) -> Result<usize, NGTQError> {
        match self.initialised_check("Failed to get queue length") {
            Ok(_) => {
                Ok(self.id_queue.len())
            },
            Err(error) => Err(error)
        }
    }

    fn get_category_queue_len(&self, category: &str) -> Result<usize, NGTQError> {
        match self.initialised_check("Failed to get queue length") {
            Ok(_) => {
                match self.category_queues.get(category) {
                    Some(queue) => Ok(queue.len()),
                    None => Err(
                        NGTQError::generate_error(
                            ngtq::NGTQErrorType::CategoryQueue(String::from("No queue found for this category")), 
                            String::from("Failed to get queue length")))
                }
            },
            Err(error) => Err(error)
        }
    }



    fn push_id_task_to_queue(&mut self, payload: String) -> Result<String, NGTQError> {
        match self.initialised_check("Failed to push new task") {
            Ok(_) => {
                let id = generate_id();
                
                if payload == String::new() {
                    Err(
                        NGTQError::generate_error(
                            ngtq::NGTQErrorType::IdQueue(String::from("The task payload is empty")), 
                            String::from("Failed to push new task")
                        )
                    )
                } else {
                    match self.id_queue.insert(id.to_string(), payload) {
                        Some(_) => Err(
                            NGTQError::generate_error(
                                ngtq::NGTQErrorType::IdQueue(String::from("A task with this id exist in the queue")),
                                String::from("Fatal Error")
                            )
                        ), 
                        None => Ok(id)
                    }
                }
            },
            Err(error) => Err(error)
        }
    }
    
    fn push_category_task_to_queue(&mut self, category: String, payload: String) -> Result<(), NGTQError> {
        match self.initialised_check("Failed to push new task") {
            Ok(_) => {
                if category == String::new() || payload == String::new() {
                    return Err(
                        NGTQError::generate_error(
                            ngtq::NGTQErrorType::CategoryQueue(String::from("The task category or payload is empty")),
                            String::from("Failed to push new task")
                        )
                    )
                } 
                match self.category_queues.get_mut(&category) {
                    Some(queue) => {
                        push_category_task_to_existing_queue(queue, payload);
                        Ok(())
                    },
                    None => {
                        let new_queue = vec![payload];
                        self.category_queues.insert(category, new_queue);
                        Ok(())
                    }
                }
            },
            Err(error) => Err(error)
        }
    }
    
    fn pull_id_task_from_queue(&mut self, id: &str) -> Result<String, NGTQError> {
        match self.initialised_check("Failed to pull task") {
            Ok(_) => {
                match self.id_queue.remove(id) {
                    Some(payload) => Ok(payload),
                    None => Err(
                        NGTQError::generate_error(
                            ngtq::NGTQErrorType::IdQueue(String::from("Task with this id was not found in queue")),
                            String::from("Failed to pull task from queue")
                        )
                    )
                }
            },
            Err(error) => Err(error)
        }
    }
    
    fn pull_category_task_from_queue(&mut self, category: &str) -> Result<String, NGTQError> {
        match self.initialised_check("Failed to pull task") {
            Ok(_) => {
                match self.category_queues.remove(category) {
                    Some(mut queue) => {
                        if queue.len() > 1 {
                            let payload = queue.remove(0);
                            self.category_queues.insert(category.to_string(), queue);
                            Ok(payload)
                        } else {
                            Ok(queue.remove(0))
                        }
                    },
                    None => Err(
                        NGTQError::generate_error(
                            ngtq::NGTQErrorType::CategoryQueue(String::from("No tasks for this topic were found")),
                            String::from("Failed to pull task from queue")
                        )
                    )
                }
            },
            Err(error) => Err(error)
        }
    }
}

fn push_category_task_to_existing_queue(queue: &mut Vec<String>, payload: String) -> usize {
    queue.push(payload);
    queue.len()
}

fn generate_id() -> String {
    IDCOUNTER.fetch_add(1, Ordering::SeqCst).to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_initialised() {
        let task_queue_arc = TaskQueue::initialise();
        match task_queue_arc.lock() {
            Ok(task_queue) => assert_eq!(task_queue.is_initialised, true),
            Err(_) => assert!(false)
        };
    }

    #[test]
    fn id_queue_len() {
        let task_queue_arc = TaskQueue::initialise();

        match task_queue_arc.lock() {
            Ok(mut task_queue) => {
                assert_eq!(task_queue.get_id_queue_len().unwrap(), 0);
                match task_queue.push_id_task_to_queue(String::from("Do Somthing")) {
                    Ok(_) => assert_eq!(task_queue.get_id_queue_len().unwrap(), 1),
                    Err(error) => {
                        println!("Test Failed - {}", error);
                        assert!(false)
                    }
                }
            },
            Err(error) => {
                println!("Test Failed -failed to open queue: {}", error);
                assert!(false)
            }
        };
    }


    #[test]
    fn category_queue_len() {
        let task_queue_arc = TaskQueue::initialise();

        match task_queue_arc.lock() {
            Ok(mut task_queue) => {
                match task_queue.push_category_task_to_queue(String::from("test"), String::from("Do Somthing")) {
                    Ok(_) => {
                        match task_queue.get_category_queue_len("test") {
                            Ok(queue_size) => assert_eq!(queue_size, 1),
                            Err(error) => {
                                println!("Test Failed - {}", error);
                                assert!(false)
                            }
                        }
                    },
                    Err(error) => {
                        println!("Test Failed - failed to push task to queue: {}", error);
                        assert!(false)
                    }
                };
            }
            Err(error) => {
                println!("Test Failed - failed to open queue: {}", error);
                assert!(false)
            }
        };
    }
}