taskfinder 2.15.0

A terminal user interface that extracts and displays tasks from plain text files, hooking into your default terminal-based editor for editing.
#![forbid(unsafe_code)]
//! Handling priorities.
//!
//! Instead of using a map of the priorities markers/Priority, we could have instead implemented
//! `Display` (as was previously done). However, that requires opening the config file every time
//! each of those functions is called, which is quite often.  

use std::collections::BTreeMap;

use crate::config::Config;

/// Priority of a file, task set, or task, from lowest to highest.
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Hash, Eq, Debug)]
pub enum Priority {
    NoPriority, // Explicitly set.
    Five,
    Four,
    Three,
    Two,
    One,
}

impl Priority {
    /// Extract the highest priority from some amount of text.
    pub fn extract(haystack: &str, priority_map: BTreeMap<Priority, String>) -> Option<Priority> {
        let mut priority = None;

        // Go through reverse order, to get highest first.
        for (p, p_string) in priority_map.into_iter().rev() {
            if haystack.contains(&p_string) {
                priority = Some(p);
                break; // return early, to return highest priority found
            }
        }
        priority
    }

    /// Display the priority marker associated with the Priority.
    pub fn as_string(&self, priority_map: BTreeMap<Priority, String>) -> String {
        priority_map.get(self).unwrap().to_string()
    }
}

/// Create map of priority markers/`Priority`.
pub fn make_priority_map(mut priority_markers: Vec<String>) -> BTreeMap<Priority, String> {
    // In the program, `priority_markers` is pulled from the Config file and there must be five
    // of them. This is enforced if the user edits them from the Config mode, but there's still
    // a chance they could edit the file manually. So, check if there's five and if not, use
    // defaults.
    if priority_markers.len() != 5 {
        priority_markers = Config::default_priority_markers();
    }
    // Create BTreeMap matching priority markers to Priorties, from highest to lowest.
    // Note that order is set by Priority's Ord, not the order in which thinks are inserted.
    let mut priority_map = BTreeMap::new();
    priority_map.insert(Priority::One, priority_markers[0].clone());
    priority_map.insert(Priority::Two, priority_markers[1].clone());
    priority_map.insert(Priority::Three, priority_markers[2].clone());
    priority_map.insert(Priority::Four, priority_markers[3].clone());
    priority_map.insert(Priority::Five, priority_markers[4].clone());
    priority_map.insert(Priority::NoPriority, "pri@0".to_string());
    priority_map
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::path::Path;

    use super::*;
    use crate::test_helpers::make_test_config;

    #[test]
    fn priority_map_is_in_correct_order() {
        let config = make_test_config();
        let priority_map = make_priority_map(config.priority_markers);
        assert_eq!(
            priority_map.first_key_value().unwrap().0,
            &Priority::NoPriority
        );
    }

    #[test]
    fn custom_priority_markers_assigned_correctly() {
        let mut test_config = make_test_config();
        test_config.priority_markers = vec![
            "!1".to_string(),
            "!2".to_string(),
            "!3".to_string(),
            "!4".to_string(),
            "!5".to_string(),
        ];

        let priority_map = make_priority_map(test_config.priority_markers);

        assert_eq!(priority_map.get(&Priority::One), Some(&"!1".to_string()));
        assert_eq!(priority_map.get(&Priority::Five), Some(&"!5".to_string()))
    }

    #[test]
    fn highest_priority_in_file_is_correct() {
        let config = make_test_config();
        let priority_map = make_priority_map(config.priority_markers);
        let priority = Priority::extract(
            &fs::read_to_string(Path::new("test_files/04_priorities.txt")).unwrap(),
            priority_map.clone(),
        );
        assert_eq!(priority, Some(Priority::One));

        let priority = Priority::extract(
            &fs::read_to_string(Path::new("test_files/01_basics.txt")).unwrap(),
            priority_map,
        );
        assert_eq!(priority, None);
    }
}