Skip to content

Commit

Permalink
feat: Labels to Vec<String> instead of HashSet<String>
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanbohan committed Jul 18, 2023
1 parent 26a7c83 commit e9f3447
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 485 deletions.
1 change: 1 addition & 0 deletions src/label/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::collections::HashSet;
use std::fmt;
use std::hash::{Hash, Hasher};

use crate::label::METRIC_NAME;
use crate::parser::token::{TokenId, T_EQL, T_EQL_REGEX, T_NEQ, T_NEQ_REGEX};
use regex::Regex;

Expand Down
63 changes: 61 additions & 2 deletions src/label/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
//! Label matchers and Well-known label names used by Prometheus components.
mod matcher;

pub use matcher::{MatchOp, Matcher, Matchers};
use std::collections::HashSet;

Expand All @@ -30,4 +29,64 @@ pub const INSTANCE_NAME: &str = "instance";

pub type Label = String;
/// Unordered set for a group of labels.
pub type Labels = HashSet<Label>;
pub type Labels = Vec<Label>;

pub fn new_labels(ls: Vec<&str>) -> Labels {
ls.iter().map(|s| s.to_string()).collect()
}

pub fn is_labels_joint(ls1: &Labels, ls2: &Labels) -> bool {
let s1: HashSet<&String> = ls1.iter().collect();
let s2: HashSet<&String> = ls2.iter().collect();

!s1.is_disjoint(&s2)
}

pub fn intersect_labels(ls1: &Labels, ls2: &Labels) -> Labels {
let s1: HashSet<&String> = ls1.iter().collect();
let s2: HashSet<&String> = ls2.iter().collect();

s1.intersection(&s2).map(|s| s.to_string()).collect()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_is_labels_joint() {
let cases = vec![
(vec!["foo"], vec!["bar"], false),
(vec!["foo"], vec!["foo", "bar"], true),
(vec!["foo"], vec!["foo"], true),
];

for (lb1, lb2, is) in cases {
let lb1: Labels = new_labels(lb1);
let lb2: Labels = new_labels(lb2);
assert_eq!(is, is_labels_joint(&lb1, &lb2), "{:?} and {:?}", lb1, lb2)
}
}

#[test]
fn test_intersect_labels() {
let cases = vec![
(vec!["foo"], vec!["bar"], vec![]),
(vec!["foo"], vec!["foo", "bar"], vec!["foo"]),
(vec!["foo"], vec!["foo"], vec!["foo"]),
(vec!["foo", "bar"], vec!["bar", "foo"], vec!["foo", "bar"]),
];

for (lb1, lb2, common) in cases {
let lb1: Labels = new_labels(lb1);
let lb2: Labels = new_labels(lb2);
let intersection: HashSet<_> = intersect_labels(&lb1, &lb2).into_iter().collect();
let expect: HashSet<_> = common.iter().map(|s| s.to_string()).collect();
assert_eq!(
expect, intersection,
"{:?} intersect {:?} does not eq {:?}",
lb1, lb2, common
)
}
}
}
144 changes: 82 additions & 62 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::label::{Labels, Matchers, METRIC_NAME};
use crate::label::{intersect_labels, is_labels_joint, new_labels, Labels, Matchers, METRIC_NAME};
use crate::parser::token::{
self, token_display, T_BOTTOMK, T_COUNT_VALUES, T_END, T_QUANTILE, T_START, T_TOPK,
};
Expand Down Expand Up @@ -56,6 +56,31 @@ impl LabelModifier {
pub fn is_on(&self) -> bool {
matches!(*self, LabelModifier::Include(_))
}

pub fn include(ls: Vec<&str>) -> Self {
Self::Include(new_labels(ls))
}

pub fn exclude(ls: Vec<&str>) -> Self {
Self::Exclude(new_labels(ls))
}
}

impl fmt::Display for LabelModifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_on() && self.labels().is_empty() {
write!(f, "")
} else {
let op = if self.is_on() { "by" } else { "without" };
let labels = self
.labels()
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>()
.join(", ");
write!(f, "{op} ({labels})")
}
}
}

/// The label list provided with the group_left or group_right modifier contains
Expand All @@ -77,6 +102,14 @@ impl VectorMatchCardinality {
VectorMatchCardinality::OneToOne => None,
}
}

pub fn many_to_one(ls: Vec<&str>) -> Self {
Self::ManyToOne(new_labels(ls))
}

pub fn one_to_many(ls: Vec<&str>) -> Self {
Self::OneToMany(new_labels(ls))
}
}

/// Binary Expr Modifier
Expand Down Expand Up @@ -174,14 +207,16 @@ impl BinModifier {
}

pub fn is_labels_joint(&self) -> bool {
matches!((self.card.labels(), &self.matching),
(Some(labels), Some(matching)) if !matching.labels().is_disjoint(labels))
matches!(
(self.card.labels(), &self.matching),
(Some(labels), Some(matching)) if is_labels_joint(matching.labels(), labels)
)
}

pub fn intersect_labels(&self) -> Option<Vec<&String>> {
pub fn intersect_labels(&self) -> Option<Vec<String>> {
if let Some(labels) = self.card.labels() {
if let Some(matching) = &self.matching {
return Some(matching.labels().intersection(labels).collect());
return Some(intersect_labels(matching.labels(), labels));
}
};
None
Expand Down Expand Up @@ -330,6 +365,34 @@ pub struct AggregateExpr {
pub modifier: Option<LabelModifier>,
}

impl fmt::Display for AggregateExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut v: Vec<String> = vec![token_display(self.op.id()).into()];

// modifier
{
if let Some(modifier) = &self.modifier {
let s = modifier.to_string();
if !s.is_empty() {
v.push(s);
}
}
}

// body
{
let mut body = String::from("(");
if let Some(param) = &self.param {
body.push_str(&format!("{param}, "));
}
body.push_str(&format!("{})", self.expr));
v.push(body)
}

write!(f, "{}", v.join(" "))
}
}

/// UnaryExpr will negate the expr
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnaryExpr {
Expand Down Expand Up @@ -374,7 +437,7 @@ impl BinaryExpr {
}

/// intersect labels of card and matching
pub fn intersect_labels(&self) -> Option<Vec<&String>> {
pub fn intersect_labels(&self) -> Option<Vec<String>> {
self.modifier
.as_ref()
.and_then(|modifier| modifier.intersect_labels())
Expand Down Expand Up @@ -892,40 +955,7 @@ impl Neg for Expr {
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Aggregate(agg) => {
let op = token_display(agg.op.id());
write!(f, "{op}")?;
if let Some(modifier) = &agg.modifier {
match modifier {
LabelModifier::Include(labels) => {
let labels = labels
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>()
.join(", ");
if !labels.is_empty() {
write!(f, " by ({labels}) ",)?;
}
}
LabelModifier::Exclude(labels) => write!(
f,
" without ({}) ",
labels
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>()
.join(", ")
)?,
}
}
write!(f, "(")?;
if let Some(param) = &agg.param {
write!(f, "{param}, ")?;
}
let expr = agg.expr.as_ref();
write!(f, "{expr})")?;
Ok(())
}
Expr::Aggregate(agg) => write!(f, "{agg}"),
Expr::Unary(UnaryExpr { expr }) => write!(f, "- {expr}"),
Expr::Binary(binary) => {
let lhs = binary.lhs.as_ref();
Expand Down Expand Up @@ -1247,8 +1277,6 @@ fn check_ast_for_vector_selector(ex: VectorSelector) -> Result<Expr, String> {

#[cfg(test)]
mod tests {
use std::collections::HashSet;

use super::*;

#[test]
Expand Down Expand Up @@ -1322,35 +1350,27 @@ mod tests {
#[test]
fn test_binary_labels() {
assert_eq!(
LabelModifier::Include(HashSet::from([String::from("foo"), String::from("bar")]))
.labels(),
&HashSet::from([String::from("foo"), String::from("bar")])
&vec![String::from("foo"), String::from("bar")],
LabelModifier::Include(vec![String::from("foo"), String::from("bar")]).labels()
);

assert_eq!(
LabelModifier::Exclude(HashSet::from([String::from("foo"), String::from("bar")]))
.labels(),
&HashSet::from([String::from("foo"), String::from("bar")])
&vec![String::from("foo"), String::from("bar")],
LabelModifier::Exclude(vec![String::from("foo"), String::from("bar")]).labels()
);

assert_eq!(
VectorMatchCardinality::OneToMany(HashSet::from([
String::from("foo"),
String::from("bar")
]))
.labels()
.unwrap(),
&HashSet::from([String::from("foo"), String::from("bar")])
&vec![String::from("foo"), String::from("bar")],
VectorMatchCardinality::OneToMany(vec![String::from("foo"), String::from("bar")])
.labels()
.unwrap()
);

assert_eq!(
VectorMatchCardinality::ManyToOne(HashSet::from([
String::from("foo"),
String::from("bar")
]))
.labels()
.unwrap(),
&HashSet::from([String::from("foo"), String::from("bar")])
&vec![String::from("foo"), String::from("bar")],
VectorMatchCardinality::ManyToOne(vec![String::from("foo"), String::from("bar")])
.labels()
.unwrap()
);

assert_eq!(VectorMatchCardinality::OneToOne.labels(), None);
Expand Down
Loading

0 comments on commit e9f3447

Please sign in to comment.