1use core::fmt;
3use k8s_openapi::apimachinery::pkg::apis::meta::v1::{LabelSelector, LabelSelectorRequirement};
4use serde::{Deserialize, Serialize};
5use std::{
6 cmp::PartialEq,
7 collections::{BTreeMap, BTreeSet},
8 fmt::Display,
9 iter::FromIterator,
10 option::IntoIter,
11};
12use thiserror::Error;
13
14mod private {
15 pub trait Sealed {}
16 impl Sealed for super::Expression {}
17 impl Sealed for super::Selector {}
18}
19
20#[derive(Debug, Error)]
21#[error("failed to parse value as expression: {0}")]
22pub struct ParseExpressionError(pub String);
24
25type Expressions = Vec<Expression>;
27
28pub trait SelectorExt: private::Sealed {
30 type Search;
32
33 fn matches(&self, on: &Self::Search) -> bool;
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
50pub enum Expression {
51 In(String, BTreeSet<String>),
61
62 NotIn(String, BTreeSet<String>),
72
73 Equal(String, String),
81
82 NotEqual(String, String),
90
91 Exists(String),
99
100 DoesNotExist(String),
108}
109
110#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Serialize)]
114pub struct Selector(Expressions);
115
116impl Selector {
117 fn from_expressions(exprs: Expressions) -> Self {
119 Self(exprs)
120 }
121
122 fn from_map(map: BTreeMap<String, String>) -> Self {
124 Self(map.into_iter().map(|(k, v)| Expression::Equal(k, v)).collect())
125 }
126
127 pub fn selects_all(&self) -> bool {
129 self.0.is_empty()
130 }
131
132 pub fn extend(&mut self, exprs: impl IntoIterator<Item = Expression>) -> &mut Self {
150 self.0.extend(exprs);
151 self
152 }
153}
154
155impl SelectorExt for Selector {
156 type Search = BTreeMap<String, String>;
157
158 fn matches(&self, labels: &BTreeMap<String, String>) -> bool {
160 for expr in self.0.iter() {
161 if !expr.matches(labels) {
162 return false;
163 }
164 }
165 true
166 }
167}
168
169impl SelectorExt for Expression {
170 type Search = BTreeMap<String, String>;
171
172 fn matches(&self, labels: &BTreeMap<String, String>) -> bool {
173 match self {
174 Expression::In(key, values) => match labels.get(key) {
175 Some(v) => values.contains(v),
176 None => false,
177 },
178 Expression::NotIn(key, values) => match labels.get(key) {
179 Some(v) => !values.contains(v),
180 None => true,
181 },
182 Expression::Exists(key) => labels.contains_key(key),
183 Expression::DoesNotExist(key) => !labels.contains_key(key),
184 Expression::Equal(key, value) => labels.get(key) == Some(value),
185 Expression::NotEqual(key, value) => labels.get(key) != Some(value),
186 }
187 }
188}
189
190impl Display for Expression {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 match self {
194 Expression::In(key, values) => {
195 write!(
196 f,
197 "{key} in ({})",
198 values.iter().cloned().collect::<Vec<_>>().join(",")
199 )
200 }
201 Expression::NotIn(key, values) => {
202 write!(
203 f,
204 "{key} notin ({})",
205 values.iter().cloned().collect::<Vec<_>>().join(",")
206 )
207 }
208 Expression::Equal(key, value) => write!(f, "{key}={value}"),
209 Expression::NotEqual(key, value) => write!(f, "{key}!={value}"),
210 Expression::Exists(key) => write!(f, "{key}"),
211 Expression::DoesNotExist(key) => write!(f, "!{key}"),
212 }
213 }
214}
215
216impl Display for Selector {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 let selectors: Vec<String> = self.0.iter().map(|e| e.to_string()).collect();
220 write!(f, "{}", selectors.join(","))
221 }
222}
223impl IntoIterator for Expression {
226 type IntoIter = IntoIter<Self::Item>;
227 type Item = Self;
228
229 fn into_iter(self) -> Self::IntoIter {
230 Some(self).into_iter()
231 }
232}
233
234impl IntoIterator for Selector {
235 type IntoIter = std::vec::IntoIter<Self::Item>;
236 type Item = Expression;
237
238 fn into_iter(self) -> Self::IntoIter {
239 self.0.into_iter()
240 }
241}
242
243impl FromIterator<(String, String)> for Selector {
244 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
245 Self::from_map(iter.into_iter().collect())
246 }
247}
248
249impl FromIterator<(&'static str, &'static str)> for Selector {
250 fn from_iter<T: IntoIterator<Item = (&'static str, &'static str)>>(iter: T) -> Self {
258 Self::from_map(
259 iter.into_iter()
260 .map(|(k, v)| (k.to_string(), v.to_string()))
261 .collect(),
262 )
263 }
264}
265
266impl FromIterator<Expression> for Selector {
267 fn from_iter<T: IntoIterator<Item = Expression>>(iter: T) -> Self {
268 Self::from_expressions(iter.into_iter().collect())
269 }
270}
271
272impl From<Expression> for Selector {
273 fn from(value: Expression) -> Self {
274 Self(vec![value])
275 }
276}
277
278impl TryFrom<LabelSelector> for Selector {
279 type Error = ParseExpressionError;
280
281 fn try_from(value: LabelSelector) -> Result<Self, Self::Error> {
282 let expressions = match value.match_expressions {
283 Some(requirements) => requirements.into_iter().map(TryInto::try_into).collect(),
284 None => Ok(vec![]),
285 }?;
286 let mut equality: Selector = value
287 .match_labels
288 .map(|labels| labels.into_iter().collect())
289 .unwrap_or_default();
290 equality.extend(expressions);
291 Ok(equality)
292 }
293}
294
295impl TryFrom<LabelSelectorRequirement> for Expression {
296 type Error = ParseExpressionError;
297
298 fn try_from(requirement: LabelSelectorRequirement) -> Result<Self, Self::Error> {
299 let key = requirement.key;
300 let values = requirement.values.map(|values| values.into_iter().collect());
301 match requirement.operator.as_str() {
302 "In" => match values {
303 Some(values) => Ok(Expression::In(key, values)),
304 None => Err(ParseExpressionError(
305 "Expected values for In operator, got none".into(),
306 )),
307 },
308 "NotIn" => match values {
309 Some(values) => Ok(Expression::NotIn(key, values)),
310 None => Err(ParseExpressionError(
311 "Expected values for In operator, got none".into(),
312 )),
313 },
314 "Exists" => Ok(Expression::Exists(key)),
315 "DoesNotExist" => Ok(Expression::DoesNotExist(key)),
316 _ => Err(ParseExpressionError("Invalid expression operator".into())),
317 }
318 }
319}
320
321impl From<Selector> for LabelSelector {
322 fn from(value: Selector) -> Self {
323 let mut equality = vec![];
324 let mut expressions = vec![];
325 for expr in value.0 {
326 match expr {
327 Expression::In(key, values) => expressions.push(LabelSelectorRequirement {
328 key,
329 operator: "In".into(),
330 values: Some(values.into_iter().collect()),
331 }),
332 Expression::NotIn(key, values) => expressions.push(LabelSelectorRequirement {
333 key,
334 operator: "NotIn".into(),
335 values: Some(values.into_iter().collect()),
336 }),
337 Expression::Equal(key, value) => equality.push((key, value)),
338 Expression::NotEqual(key, value) => expressions.push(LabelSelectorRequirement {
339 key,
340 operator: "NotIn".into(),
341 values: Some(vec![value]),
342 }),
343 Expression::Exists(key) => expressions.push(LabelSelectorRequirement {
344 key,
345 operator: "Exists".into(),
346 values: None,
347 }),
348 Expression::DoesNotExist(key) => expressions.push(LabelSelectorRequirement {
349 key,
350 operator: "DoesNotExist".into(),
351 values: None,
352 }),
353 }
354 }
355
356 LabelSelector {
357 match_labels: (!equality.is_empty()).then_some(equality.into_iter().collect()),
358 match_expressions: (!expressions.is_empty()).then_some(expressions),
359 }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use std::iter::FromIterator;
367
368 #[test]
369 fn test_raw_matches() {
370 for (selector, label_selector, labels, matches, msg) in &[
371 (
372 Selector::default(),
373 LabelSelector::default(),
374 Default::default(),
375 true,
376 "empty match",
377 ),
378 (
379 Selector::from_iter(Some(("foo", "bar"))),
380 LabelSelector {
381 match_labels: Some([("foo".into(), "bar".into())].into()),
382 match_expressions: Default::default(),
383 },
384 [("foo".to_string(), "bar".to_string())].into(),
385 true,
386 "exact label match",
387 ),
388 (
389 Selector::from_iter(Some(("foo", "bar"))),
390 LabelSelector {
391 match_labels: Some([("foo".to_string(), "bar".to_string())].into()),
392 match_expressions: None,
393 },
394 [
395 ("foo".to_string(), "bar".to_string()),
396 ("bah".to_string(), "baz".to_string()),
397 ]
398 .into(),
399 true,
400 "sufficient label match",
401 ),
402 (
403 Selector::from_iter(Some(Expression::In(
404 "foo".into(),
405 Some("bar".to_string()).into_iter().collect(),
406 ))),
407 LabelSelector {
408 match_labels: None,
409 match_expressions: Some(vec![LabelSelectorRequirement {
410 key: "foo".into(),
411 operator: "In".to_string(),
412 values: Some(vec!["bar".into()]),
413 }]),
414 },
415 [
416 ("foo".to_string(), "bar".to_string()),
417 ("bah".to_string(), "baz".to_string()),
418 ]
419 .into(),
420 true,
421 "In expression match",
422 ),
423 (
424 Selector::from_iter(Some(Expression::Equal(
425 "foo".into(),
426 Some("bar".to_string()).into_iter().collect(),
427 ))),
428 LabelSelector {
429 match_labels: Some([("foo".into(), "bar".into())].into()),
430 match_expressions: None,
431 },
432 [
433 ("foo".to_string(), "bar".to_string()),
434 ("bah".to_string(), "baz".to_string()),
435 ]
436 .into(),
437 true,
438 "Equal expression match",
439 ),
440 (
441 Selector::from_iter(Some(Expression::NotEqual(
442 "foo".into(),
443 Some("bar".to_string()).into_iter().collect(),
444 ))),
445 LabelSelector {
446 match_labels: None,
447 match_expressions: Some(vec![LabelSelectorRequirement {
448 key: "foo".into(),
449 operator: "NotIn".into(),
450 values: Some(vec!["bar".into()]),
451 }]),
452 },
453 [
454 ("foo".to_string(), "bar".to_string()),
455 ("bah".to_string(), "baz".to_string()),
456 ]
457 .into(),
458 false,
459 "NotEqual expression match",
460 ),
461 (
462 Selector::from_iter(Some(Expression::In(
463 "foo".into(),
464 Some("bar".to_string()).into_iter().collect(),
465 ))),
466 LabelSelector {
467 match_labels: None,
468 match_expressions: Some(vec![LabelSelectorRequirement {
469 key: "foo".into(),
470 operator: "In".into(),
471 values: Some(vec!["bar".into()]),
472 }]),
473 },
474 [
475 ("foo".to_string(), "bar".to_string()),
476 ("bah".to_string(), "baz".to_string()),
477 ]
478 .into(),
479 true,
480 "In expression match",
481 ),
482 (
483 Selector::from_iter(Some(Expression::NotIn(
484 "foo".into(),
485 Some("quux".to_string()).into_iter().collect(),
486 ))),
487 LabelSelector {
488 match_labels: None,
489 match_expressions: Some(vec![LabelSelectorRequirement {
490 key: "foo".into(),
491 operator: "NotIn".into(),
492 values: Some(vec!["quux".into()]),
493 }]),
494 },
495 [
496 ("foo".to_string(), "bar".to_string()),
497 ("bah".to_string(), "baz".to_string()),
498 ]
499 .into(),
500 true,
501 "NotIn expression match",
502 ),
503 (
504 Selector::from_iter(Some(Expression::NotIn(
505 "foo".into(),
506 Some("bar".to_string()).into_iter().collect(),
507 ))),
508 LabelSelector {
509 match_labels: None,
510 match_expressions: Some(vec![LabelSelectorRequirement {
511 key: "foo".into(),
512 operator: "NotIn".into(),
513 values: Some(vec!["bar".into()]),
514 }]),
515 },
516 [
517 ("foo".to_string(), "bar".to_string()),
518 ("bah".to_string(), "baz".to_string()),
519 ]
520 .into(),
521 false,
522 "NotIn expression non-match",
523 ),
524 (
525 Selector(vec![
526 Expression::Equal("foo".to_string(), "bar".to_string()),
527 Expression::In("bah".into(), Some("bar".to_string()).into_iter().collect()),
528 ]),
529 LabelSelector {
530 match_labels: Some([("foo".into(), "bar".into())].into()),
531 match_expressions: Some(vec![LabelSelectorRequirement {
532 key: "bah".into(),
533 operator: "In".into(),
534 values: Some(vec!["bar".into()]),
535 }]),
536 },
537 [
538 ("foo".to_string(), "bar".to_string()),
539 ("bah".to_string(), "baz".to_string()),
540 ]
541 .into(),
542 false,
543 "matches labels but not expressions",
544 ),
545 (
546 Selector(vec![
547 Expression::Equal("foo".to_string(), "bar".to_string()),
548 Expression::In("bah".into(), Some("bar".to_string()).into_iter().collect()),
549 ]),
550 LabelSelector {
551 match_labels: Some([("foo".into(), "bar".into())].into()),
552 match_expressions: Some(vec![LabelSelectorRequirement {
553 key: "bah".into(),
554 operator: "In".into(),
555 values: Some(vec!["bar".into()]),
556 }]),
557 },
558 [
559 ("foo".to_string(), "bar".to_string()),
560 ("bah".to_string(), "bar".to_string()),
561 ]
562 .into(),
563 true,
564 "matches both labels and expressions",
565 ),
566 ] {
567 assert_eq!(selector.matches(labels), *matches, "{}", msg);
568 let converted: LabelSelector = selector.clone().into();
569 assert_eq!(&converted, label_selector);
570 let converted_selector: Selector = converted.try_into().unwrap();
571 assert_eq!(
572 converted_selector.matches(labels),
573 *matches,
574 "After conversion: {}",
575 msg
576 );
577 }
578 }
579
580 #[test]
581 fn test_label_selector_matches() {
582 let selector: Selector = LabelSelector {
583 match_expressions: Some(vec![
584 LabelSelectorRequirement {
585 key: "foo".into(),
586 operator: "In".into(),
587 values: Some(vec!["bar".into()]),
588 },
589 LabelSelectorRequirement {
590 key: "foo".into(),
591 operator: "NotIn".into(),
592 values: Some(vec!["baz".into()]),
593 },
594 LabelSelectorRequirement {
595 key: "foo".into(),
596 operator: "Exists".into(),
597 values: None,
598 },
599 LabelSelectorRequirement {
600 key: "baz".into(),
601 operator: "DoesNotExist".into(),
602 values: None,
603 },
604 ]),
605 match_labels: Some([("foo".into(), "bar".into())].into()),
606 }
607 .try_into()
608 .unwrap();
609 assert!(selector.matches(&[("foo".into(), "bar".into())].into()));
610 assert!(!selector.matches(&Default::default()));
611 }
612
613 #[test]
614 fn test_to_string() {
615 let selector = Selector(vec![
616 Expression::In("foo".into(), ["bar".into(), "baz".into()].into()),
617 Expression::NotIn("foo".into(), ["bar".into(), "baz".into()].into()),
618 Expression::Equal("foo".into(), "bar".into()),
619 Expression::NotEqual("foo".into(), "bar".into()),
620 Expression::Exists("foo".into()),
621 Expression::DoesNotExist("foo".into()),
622 ])
623 .to_string();
624
625 assert_eq!(
626 selector,
627 "foo in (bar,baz),foo notin (bar,baz),foo=bar,foo!=bar,foo,!foo"
628 )
629 }
630}