1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use soulog::*;
use crate::{archive::Archive, entry::Entry, moc::MOC, sort};

pub trait Searchable {
    fn get_uid(&self) -> String;
    #[allow(clippy::ptr_arg)]
    fn contains_tag(&mut self, tag: &String, logger: impl Logger) -> bool;
}

pub fn search_strict(tags: &[String], items: Vec<impl Searchable>, logger: impl Logger) -> Vec<String> {
    let mut result = Vec::new();
    for mut item in items.into_iter() {
        let mut all_tags_present = true;
        for tag in tags.iter() {
            if !item.contains_tag(tag, logger.hollow()) {
                all_tags_present = false;
                break;
            }
        } if all_tags_present {
            result.push(item.get_uid());
        }
    }

    result
}

pub fn search<T: Searchable>(tags: &[String], items: Vec<T>, logger: impl Logger) -> Vec<String> {
    let mut result = Vec::new();
    for mut item in items.into_iter() {
        for tag in tags {
            if item.contains_tag(tag, logger.hollow()) {
                result.push(item.get_uid());
                break;
            }
        }
    }
    result
}

pub fn list_command(strict: bool, show_mocs: bool, show_entries: bool, filter: Option<Vec<String>>, mut logger: impl Logger) {
    let archive = Archive::load(logger.hollow());

    // Get entries and mocs
    sort::sort(logger.hollow());
    let mut entries: Vec<_> = sort::read_sorted(&archive, logger.hollow())
        .into_vec()
        .into_iter()
        .map(|x| archive.get_entry(x, logger.hollow()).unwrap())
        .collect();

    let mut mocs = archive.list_mocs(logger.hollow());

    let filter = match filter {
        Some(x) => x,
        None => {
            log!((logger) List("Listing selected items..."));

            let tags = get_unique_tags(&mut entries, &mut mocs, logger.hollow());

            log!((logger.vital) tags("{tags:#?}") as Result);
            std::mem::drop(tags);

            let entry_uids: Vec<String> = entries.into_iter().map(|e| e.uid).collect();
            let moc_uids: Vec<String> = mocs.into_iter().map(|m| m.uid).collect();

            if show_entries { log!((logger.vital) entries("{entry_uids:#?}") as Result) }
            if show_mocs { log!((logger.vital) mocs("{moc_uids:#?}") as Result) }
            return;
        }
    };

    let entry_uids: Vec<String>;
    let moc_uids: Vec<String>;

    if strict {
        log!((logger) List("Searching strictly with tags {filter:?} in mocs and entries..."));
        entry_uids = search_strict(&filter, entries, logger.hollow());
        moc_uids = search_strict(&filter, mocs, logger.hollow());
    } else {
        log!((logger) List("Searching with tags {filter:?} in mocs and entries..."));
        entry_uids = search(&filter, entries, logger.hollow());
        moc_uids = search(&filter, mocs, logger.hollow());
    }

    log!((logger) List("Listing found entries and mocs..."));

    log!((logger.vital) tags("{filter:?}") as Result);
    if show_entries { log!((logger.vital) entries("{entry_uids:?}") as Result) }
    if show_mocs { log!((logger.vital) mocs("{moc_uids:?}") as Result) }
}

use std::collections::HashSet;
fn get_unique_tags<'a>(entries: &'a mut [Entry], mocs: &'a mut [MOC], logger: impl Logger) -> HashSet<&'a String> {
    let mut tags = HashSet::new();

    tags.extend(entries.iter_mut().flat_map(|x| x.tags(logger.hollow()).iter()));
    tags.extend(mocs.iter_mut().flat_map(|x| x.tags(logger.hollow()).iter()));

    tags
}