1use super::prelude::*;
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
8pub struct TagSet(pub Vec<String>);
9
10impl TagSet {
11 pub const fn new() -> Self {
13 Self(Vec::new())
14 }
15 pub fn filter<P>(&self, pred: P) -> Self
17 where
18 P: Fn(&&String) -> bool,
19 {
20 TagSet(self.0.iter().filter(pred).map(|t| t.to_string()).collect())
21 }
22 pub fn iter(&self) -> core::slice::Iter<'_, String> {
24 self.0.iter()
25 }
26 pub fn contains(&self, tag: &String) -> bool {
28 self.0.contains(tag)
29 }
30 pub fn is_empty(&self) -> bool {
32 self.0.is_empty()
33 }
34 pub fn len(&self) -> usize {
36 self.0.len()
37 }
38 pub fn modify(&self, modification: &TagSet) -> TagSet {
40 if modification
42 .iter()
43 .any(|tag| tag.starts_with('+') || tag.ends_with('+'))
44 {
45 let mut tags = self.clone();
46 for tag in modification.iter() {
47 if tag.is_empty() {
48 continue;
50 } else if tag.starts_with('-') || tag.ends_with('-') {
51 tags.remove(tag[1..].into());
52 } else {
53 tags.insert(tag[1..].into());
54 }
55 }
56 tags
57 } else {
58 modification.clone()
59 }
60 }
61}
62
63impl TagSet {
64 pub fn insert(&mut self, tag: &str) -> bool {
66 if self.0.contains(&tag.to_string()) {
67 false
68 } else {
69 self.0.push(tag.to_string());
70 true
71 }
72 }
73 pub fn insert_many(&mut self, tags: TagSet) {
75 for tag in tags.iter() {
76 self.insert(tag);
77 }
78 }
79 pub fn remove(&mut self, tag: &str) {
81 self.0 = self
82 .0
83 .iter()
84 .filter(|t| *t != tag)
85 .map(|t| t.to_string())
86 .collect();
87 }
88}
89impl std::fmt::Display for TagSet {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 for (n, tag) in self.0.iter().enumerate() {
92 tags::format(f, tag)?;
93 if n + 1 < self.0.len() {
94 write!(f, ", ")?;
95 }
96 }
97 Ok(())
98 }
99}
100
101impl From<Option<TagSet>> for TagSet {
102 fn from(tags: Option<TagSet>) -> Self {
104 if let Some(tags) = tags {
105 tags.clone()
106 } else {
107 TagSet::new()
108 }
109 }
110}
111
112impl From<&Option<String>> for TagSet {
113 fn from(tag: &Option<String>) -> Self {
115 if let Some(tag) = tag {
116 Self(tag.split(',').map(|t| t.to_string()).collect())
117 } else {
118 Self(Vec::new())
119 }
120 }
121}
122impl From<&str> for TagSet {
123 fn from(tag: &str) -> Self {
125 assert!(!tag.contains(' '));
126 Self(tag.split(',').map(|t| t.to_string()).collect())
127 }
128}