1use super::{Group, Tag};
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5use alloc::string::{String, ToString};
6use core::borrow::Borrow;
7
8mod sealed {
9 use super::*;
10
11 pub trait Sealed {}
12
13 impl Sealed for Tag {}
14 impl Sealed for [Tag] {}
15 impl<const N: usize> Sealed for [Tag; N] {}
16 impl Sealed for TagPredicate {}
17}
18
19pub trait TagPattern: sealed::Sealed {
21 fn match_tags<T, I>(&self, tags: I) -> bool
23 where
24 T: Borrow<Tag>,
25 I: IntoIterator<Item = T>;
26}
27
28impl TagPattern for Tag {
29 fn match_tags<T, I>(&self, tags: I) -> bool
30 where
31 T: Borrow<Tag>,
32 I: IntoIterator<Item = T>,
33 {
34 tags.into_iter().any(|tag| tag.borrow() == self)
35 }
36}
37
38impl TagPattern for [Tag] {
39 fn match_tags<T, I>(&self, tags: I) -> bool
40 where
41 T: Borrow<Tag>,
42 I: IntoIterator<Item = T>,
43 {
44 let tags = tags.into_iter().collect::<Vec<_>>();
45 self.iter()
46 .all(|tag| tags.iter().any(|t| tag == t.borrow()))
47 }
48}
49
50impl<const N: usize> TagPattern for [Tag; N] {
51 fn match_tags<T, I>(&self, tags: I) -> bool
52 where
53 T: Borrow<Tag>,
54 I: IntoIterator<Item = T>,
55 {
56 <[Tag]>::match_tags(self, tags)
57 }
58}
59
60#[derive(Debug, PartialEq)]
62pub enum TagPredicate {
63 And(Vec<TagPredicate>),
65 Or(Vec<TagPredicate>),
67 Not(Box<TagPredicate>),
69
70 Group(Group),
72 Name(String),
74 Tag(Tag),
76}
77
78impl From<Tag> for TagPredicate {
79 fn from(tag: Tag) -> TagPredicate {
80 TagPredicate::Tag(tag)
81 }
82}
83
84impl From<Group> for TagPredicate {
85 fn from(group: Group) -> Self {
86 TagPredicate::Group(group)
87 }
88}
89
90impl TagPredicate {
91 pub fn and<T, I>(preds: I) -> TagPredicate
93 where
94 T: Into<TagPredicate>,
95 I: IntoIterator<Item = T>,
96 {
97 TagPredicate::And(preds.into_iter().map(T::into).collect())
98 }
99
100 pub fn or<T, I>(preds: I) -> TagPredicate
102 where
103 T: Into<TagPredicate>,
104 I: IntoIterator<Item = T>,
105 {
106 TagPredicate::Or(preds.into_iter().map(T::into).collect())
107 }
108
109 pub fn not<T>(pred: T) -> TagPredicate
111 where
112 T: Into<TagPredicate>,
113 {
114 TagPredicate::Not(Box::new(pred.into()))
115 }
116
117 pub fn group(group: Group) -> TagPredicate {
119 TagPredicate::Group(group)
120 }
121
122 pub fn name(name: &str) -> TagPredicate {
124 TagPredicate::Name(name.to_string())
125 }
126
127 pub fn tag(tag: Tag) -> TagPredicate {
129 TagPredicate::Tag(tag)
130 }
131}
132
133impl TagPattern for TagPredicate {
134 fn match_tags<T, I>(&self, tags: I) -> bool
135 where
136 T: Borrow<Tag>,
137 I: IntoIterator<Item = T>,
138 {
139 use TagPredicate::*;
140
141 let mut iter = tags.into_iter();
142 match self {
143 And(preds) => {
144 let tags = iter.collect::<Vec<_>>();
145 preds.iter().all(|pred| {
146 pred.match_tags(tags.iter().map(Borrow::borrow))
147 })
148 }
149 Or(preds) => {
150 let tags = iter.collect::<Vec<_>>();
151 preds.iter().any(|pred| {
152 pred.match_tags(tags.iter().map(Borrow::borrow))
153 })
154 }
155 Not(pred) => !pred.match_tags(iter),
156
157 Group(group) => iter.any(|tag| tag.borrow().group() == group),
158 Name(name) => iter.any(|tag| tag.borrow().name() == name),
159 Tag(tag) => tag.match_tags(iter),
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_tag() {
170 let tag_a = Tag::named("a");
171
172 assert!(tag_a.match_tags(&[Tag::named("a"), Tag::named("b"),]));
173 assert!(!tag_a.match_tags(&[Tag::named("b"), Tag::named("c"),]));
174 }
175
176 #[test]
177 fn test_tag_slice() {
178 let tag_slice = &[Tag::named("a"), Tag::named("b")];
179
180 assert!(tag_slice.match_tags(&[
181 Tag::named("b"),
182 Tag::named("c"),
183 Tag::named("d"),
184 Tag::named("a"),
185 ]));
186
187 assert!(!tag_slice.match_tags(&[Tag::named("c"), Tag::named("ab"), Tag::named("d"),]))
188 }
189
190 #[test]
191 fn test_pred_and() {
192 let pred = TagPredicate::and([Tag::named("a"), Tag::named("b")]);
193
194 assert!(pred.match_tags(&[
195 Tag::named("c"),
196 Tag::named("b"),
197 Tag::named("f"),
198 Tag::named("a"),
199 ]));
200 assert!(!pred.match_tags(&[Tag::named("c"), Tag::named("f"), Tag::named("a"),]));
201 }
202
203 #[test]
204 fn test_pred_or() {
205 let pred = TagPredicate::or([Tag::named("a"), Tag::named("b")]);
206
207 assert!(pred.match_tags(&[Tag::named("a"),]));
208 assert!(pred.match_tags(&[Tag::named("b"), Tag::named("c"),]));
209 assert!(!pred.match_tags(&[Tag::named("c"), Tag::named("d"),]));
210 }
211
212 #[test]
213 fn test_pred_not() {
214 let pred = TagPredicate::not(Tag::named("a"));
215
216 assert!(pred.match_tags(&[Tag::named("b"), Tag::named("c")]));
217 assert!(!pred.match_tags(&[Tag::named("a"), Tag::named("b")]));
218 }
219
220 #[test]
221 fn test_pred_group() {
222 let pred = TagPredicate::group(Group::Default);
223
224 assert!(pred.match_tags(&[Tag::named("a"), Tag::new(Group::custom("group"), "a")]));
225 assert!(!pred.match_tags(&[
226 Tag::new(Group::custom("group"), "a"),
227 Tag::new(Group::custom("group"), "b")
228 ]));
229 }
230
231 #[test]
232 fn test_pred_name() {
233 let pred = TagPredicate::name("a");
234
235 assert!(pred.match_tags(&[
236 Tag::new(Group::custom("group"), "a"),
237 Tag::new(Group::custom("group"), "b"),
238 ]));
239 assert!(pred.match_tags(&[Tag::named("a"), Tag::named("b"),]));
240 assert!(!pred.match_tags(&[Tag::new(Group::custom("group"), "b"), Tag::named("b"),]));
241 }
242
243 #[test]
244 fn test_pred_tag() {
245 let pred = TagPredicate::Tag(Tag::named("a"));
246
247 assert!(pred.match_tags(&[Tag::named("c"), Tag::named("a"),]));
248 assert!(!pred.match_tags(&[Tag::named("c"), Tag::named("f"),]));
249 }
250}