From a6612c24db670169d7f00cea6bb976bc37bd485a Mon Sep 17 00:00:00 2001
From: Maaz Ahmed <maaz.a@subcom.tech>
Date: Wed, 27 Dec 2023 08:42:02 +0000
Subject: [PATCH] feat: add support for more functions (transform)

---
 src/query/fns/mod.rs       |   1 -
 src/query/fns/transform.rs | 124 ++++++++++++++++++++++++++++++++++++-
 tests/fns.rs               |  24 +++++++
 3 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/src/query/fns/mod.rs b/src/query/fns/mod.rs
index 6314ea1..0b10770 100644
--- a/src/query/fns/mod.rs
+++ b/src/query/fns/mod.rs
@@ -19,7 +19,6 @@ pub mod aggregate;
 #[cfg(feature = "metricsql")]
 pub mod label;
 pub mod rollup;
-#[cfg(feature = "metricsql")]
 pub mod transform;
 
 // This macro takes a name and formatting arguments which are directly passed to the `write` macro
diff --git a/src/query/fns/transform.rs b/src/query/fns/transform.rs
index 7a8b1e8..9e2f3a8 100644
--- a/src/query/fns/transform.rs
+++ b/src/query/fns/transform.rs
@@ -5,6 +5,7 @@ use crate::query::ops::Operable;
 use super::{qry_fn, QryFunc};
 
 /// MetricsQL `limit_offset` query function
+#[cfg(feature = "metricsql")]
 pub fn mql_limit_offset(
     limit: usize,
     offset: usize,
@@ -14,6 +15,7 @@ pub fn mql_limit_offset(
 }
 
 /// MetricsQL `union` query function
+#[cfg(feature = "metricsql")]
 pub fn mql_union(
     qry_expressions: &[impl Operable],
 ) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result + '_> {
@@ -28,11 +30,131 @@ pub fn mql_union(
     )
 }
 
+/// The `round` query transform function
+pub fn round(
+    vec_expr: impl Operable + 'static,
+    nearest: Option<f64>,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(round, {
+        |f| {
+            if let Some(near) = nearest {
+                return write!(f, "{vec_expr}, {near}");
+            }
+            write!(f, "{vec_expr}")
+        }
+    })
+}
+
+/// The `abs` query transform function
+pub fn abs(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(abs, "{vec_expr}")
+}
+
+/// The `absent` query transform function
+pub fn absent(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(absent, "{vec_expr}")
+}
+
+/// The `ceil` query transform function
+pub fn ceil(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(ceil, "{vec_expr}")
+}
+
+/// The `changes` query transform function
+pub fn changes(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(changes, "{vec_expr}")
+}
+
+/// The `delta` query transform function
+pub fn delta(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(delta, "{vec_expr}")
+}
+/// The `exp` query transform function
+pub fn exp(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(exp, "{vec_expr}")
+}
+/// The `floor` query transform function
+pub fn floor(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(floor, "{vec_expr}")
+}
+
+/// The `sort` query transform function
+pub fn sort(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(sort, "{vec_expr}")
+}
+
+/// The `sort_desc` query transform function
+pub fn sort_desc(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(sort_desc, "{vec_expr}")
+}
+
+/// The `sqrt` query transform function
+pub fn sqrt(
+    vec_expr: impl Operable + 'static,
+) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(sqrt, "{vec_expr}")
+}
+
+/// The `vector` query transform function
+pub fn vector(scalar: f64) -> QryFunc<impl Fn(&mut fmt::Formatter) -> fmt::Result> {
+    qry_fn!(vector, "{scalar}")
+}
+
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::query::Metric;
 
+    macro_rules! test_transform {
+        ($($name:tt),+) => {
+            $(
+            #[test]
+            fn $name() {
+                let qry = super::$name(Metric::new("metric")).to_string();
+                assert_eq!(qry, concat!(stringify!($name), "(metric)"))
+            }
+            )+
+        };
+    }
+
+    #[test]
+    fn round() {
+        let qry = super::round(Metric::new("metric"), Some(2.0)).to_string();
+        assert_eq!(qry, "round(metric, 2)");
+    }
+
+    #[test]
+    fn vector() {
+        let qry = super::vector(1.5).to_string();
+        assert_eq!(qry, "vector(1.5)");
+    }
+
+    test_transform!(abs, absent, ceil, changes, delta, exp, floor, sort, sort_desc, sqrt);
+}
+
+#[cfg(all(test, feature = "metricsql"))]
+mod mql_tests {
+    use crate::query::Metric;
+
+    use super::*;
+
     #[test]
     fn trans_limit_offset() {
         let query = mql_limit_offset(1, 2, Metric::new("metric")).to_string();
diff --git a/tests/fns.rs b/tests/fns.rs
index 15c2521..da305a8 100644
--- a/tests/fns.rs
+++ b/tests/fns.rs
@@ -34,6 +34,16 @@ macro_rules! label_tests {
     };
 }
 
+macro_rules! transform_tests {
+    ($($fn_name: tt),+) => {
+        $(#[tokio::test]
+        async fn $fn_name() {
+            let query = transform::$fn_name(Metric::new("metric"));
+            utils::send_query(query).await.unwrap();
+        })+
+    };
+}
+
 aggr_tests!(sum, min, max, avg, group, stddev, stdvar, count);
 
 #[tokio::test]
@@ -98,6 +108,8 @@ label_tests!(
     mql_sort_by_label_numeric_desc
 );
 
+transform_tests!(abs, absent, ceil, changes, delta, exp, floor, sort, sort_desc, sqrt);
+
 #[tokio::test]
 async fn trans_limit_offset() {
     let query = transform::mql_limit_offset(1, 2, Metric::new("metric")).to_string();
@@ -109,3 +121,15 @@ async fn trans_union() {
     let query = transform::mql_union(&[Metric::new("metric"), Metric::new("metric2")]).to_string();
     utils::send_query(query).await.unwrap();
 }
+
+#[tokio::test]
+async fn trans_round() {
+    let query = transform::round(Metric::new("metric"), Some(1.0));
+    utils::send_query(query).await.unwrap();
+}
+
+#[tokio::test]
+async fn trans_vector() {
+    let query = transform::vector(1.0);
+    utils::send_query(query).await.unwrap();
+}
-- 
GitLab