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
import_stdlib!();

use crate::Tag;

/// A type that can map between tags and their names.
pub trait TagsStoreTrait {
    fn assigned_name_for_tag(&self, tag: &Tag) -> Option<String>;
    fn name_for_tag(&self, tag: &Tag) -> String;
    fn tag_for_value(&self, value: u64) -> Option<Tag>;
    fn tag_for_name(&self, name: &str) -> Option<Tag>;

    fn name_for_tag_opt<T>(tag: &Tag, tags: Option<&T>) -> String where T: TagsStoreTrait, Self: Sized {
        match tags {
            None => tag.value().to_string(),
            Some(tags) => tags.name_for_tag(tag)
        }
    }
}

/// A dictionary of mappings between tags and their names.
#[derive(Clone, Debug)]
pub struct TagsStore {
    tags_by_value: HashMap<u64, Tag>,
    tags_by_name: HashMap<String, Tag>,
}

impl TagsStore {
    pub fn new<T>(tags: T) -> Self where T: IntoIterator<Item=Tag> {
        let mut tags_by_value = HashMap::new();
        let mut tags_by_name = HashMap::new();
        for tag in tags {
            Self::_insert(tag, &mut tags_by_value, &mut tags_by_name);
        }
        Self {
            tags_by_value,
            tags_by_name,
        }
    }

    pub fn insert(&mut self, tag: Tag) {
        Self::_insert(tag, &mut self.tags_by_value, &mut self.tags_by_name);
    }

    fn _insert(tag: Tag, tags_by_value: &mut HashMap<u64, Tag>, tags_by_name: &mut HashMap<String, Tag>) {
        let name = tag.name().unwrap();
        assert!(!name.is_empty());
        tags_by_value.insert(tag.value(), tag.clone());
        tags_by_name.insert(name, tag);
    }
}

impl TagsStoreTrait for TagsStore {
    fn assigned_name_for_tag(&self, tag: &Tag) -> Option<String> {
        self.tag_for_value(tag.value()).map(|tag| tag.name().unwrap())
    }

    fn name_for_tag(&self, tag: &Tag) -> String {
        self.assigned_name_for_tag(tag).unwrap_or_else(|| tag.value().to_string())
    }

    fn tag_for_name(&self, name: &str) -> Option<Tag> {
        self.tags_by_name.get(name).cloned()
    }

    fn tag_for_value(&self, value: u64) -> Option<Tag> {
        self.tags_by_value.get(&value).cloned()
    }
}

impl Default for TagsStore {
    fn default() -> Self {
        Self::new([])
    }
}