From 76912470f423e944b2c9ffc2e265641cfbb4e6b8 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 23 Oct 2024 07:25:05 -0700 Subject: [PATCH] feat: serialize AST to json (#94) * feat: serialize AST to json * chore: make header check happy again * feat: implement serialize for vectorSelector and call * test: improve converage * feat: implement serialization for more * test: cover more expression * fix: subquery * refactor: address review comments --- Cargo.toml | 6 + src/label/matcher.rs | 15 ++ src/label/mod.rs | 17 ++ src/parser/ast.rs | 183 +++++++++++++++++- src/parser/function.rs | 19 ++ src/parser/token.rs | 10 + src/parser/value.rs | 2 + src/util/duration.rs | 24 +++ tests/ser.rs | 428 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 703 insertions(+), 1 deletion(-) create mode 100644 tests/ser.rs diff --git a/Cargo.toml b/Cargo.toml index 3d11101..64b327b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,12 @@ lazy_static = "1.4.0" lrlex = "0.13.5" lrpar = "0.13.5" regex = "1" +serde = { version = "1", optional = true } +serde_json = { version = "1", optional = true } + +[features] +default = [] +ser = ["serde", "serde_json"] [build-dependencies] cfgrammar = "0.13.5" diff --git a/src/label/matcher.rs b/src/label/matcher.rs index 2e009f4..5764d01 100644 --- a/src/label/matcher.rs +++ b/src/label/matcher.rs @@ -24,6 +24,7 @@ use crate::util::join_vector; pub enum MatchOp { Equal, NotEqual, + // TODO: do we need regex here? Re(Regex), NotRe(Regex), } @@ -64,9 +65,21 @@ impl Hash for MatchOp { } } +#[cfg(feature = "ser")] +impl serde::Serialize for MatchOp { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + // Matcher models the matching of a label. #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "ser", derive(serde::Serialize))] pub struct Matcher { + #[cfg_attr(feature = "ser", serde(rename = "type"))] pub op: MatchOp, pub name: String, pub value: String, @@ -183,8 +196,10 @@ fn try_escape_for_repeat_re(re: &str) -> String { } #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ser", derive(serde::Serialize))] pub struct Matchers { pub matchers: Vec, + #[cfg_attr(feature = "ser", serde(skip_serializing_if = "<[_]>::is_empty"))] pub or_matchers: Vec>, } diff --git a/src/label/mod.rs b/src/label/mod.rs index 1bba6d2..8549816 100644 --- a/src/label/mod.rs +++ b/src/label/mod.rs @@ -73,6 +73,23 @@ impl fmt::Display for Labels { } } +#[cfg(feature = "ser")] +impl serde::Serialize for Labels { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(self.labels.len()))?; + + for l in &self.labels { + seq.serialize_element(&l)?; + } + + seq.end() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 2131388..141ad20 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -43,6 +43,7 @@ use std::time::{Duration, SystemTime}; /// /// if empty listed labels, meaning no grouping #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ser", derive(serde::Serialize))] pub enum LabelModifier { Include(Labels), Exclude(Labels), @@ -72,6 +73,8 @@ impl LabelModifier { /// The label list provided with the group_left or group_right modifier contains /// additional labels from the "one"-side to be included in the result metrics. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ser", derive(serde::Serialize))] +#[cfg_attr(feature = "ser", serde(rename_all = "kebab-case"))] pub enum VectorMatchCardinality { OneToOne, ManyToOne(Labels), @@ -197,12 +200,91 @@ impl BinModifier { } } +#[cfg(feature = "ser")] +pub(crate) fn serialize_bin_modifier( + this: &Option, + serializer: S, +) -> Result +where + S: serde::Serializer, +{ + use serde::ser::SerializeMap; + use serde_json::json; + + let mut map = serializer.serialize_map(Some(2))?; + + map.serialize_entry( + "bool", + &this.as_ref().map(|t| t.return_bool).unwrap_or(false), + )?; + if let Some(t) = this { + if let Some(labels) = &t.matching { + map.serialize_key("matching")?; + + match labels { + LabelModifier::Include(labels) => { + let value = json!({ + "card": t.card, + "include": [], + "labels": labels, + "on": true, + }); + map.serialize_value(&value)?; + } + LabelModifier::Exclude(labels) => { + let value = json!({ + "card": t.card, + "include": [], + "labels": labels, + "on": false, + }); + map.serialize_value(&value)?; + } + } + } else { + let value = json!({ + "card": t.card, + "include": [], + "labels": [], + "on": false, + }); + map.serialize_entry("matching", &value)?; + } + } else { + map.serialize_entry("matching", &None::)?; + } + + map.end() +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Offset { Pos(Duration), Neg(Duration), } +impl Offset { + #[cfg(feature = "ser")] + pub(crate) fn as_millis(&self) -> i128 { + match self { + Self::Pos(dur) => dur.as_millis() as i128, + Self::Neg(dur) => -(dur.as_millis() as i128), + } + } + + #[cfg(feature = "ser")] + pub(crate) fn serialize_offset( + offset: &Option, + serializer: S, + ) -> Result + where + S: serde::Serializer, + { + let value = offset.as_ref().map(|o| o.as_millis()).unwrap_or(0); + serializer.serialize_i128(value) + } +} + impl fmt::Display for Offset { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -211,6 +293,7 @@ impl fmt::Display for Offset { } } } + #[derive(Debug, Clone, PartialEq, Eq)] pub enum AtModifier { Start, @@ -233,6 +316,40 @@ impl fmt::Display for AtModifier { } } } + +#[cfg(feature = "ser")] +impl serde::Serialize for AtModifier { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(Some(2))?; + match self { + AtModifier::Start => { + map.serialize_entry("startOrEnd", &Some("start"))?; + map.serialize_entry("timestamp", &None::)?; + } + AtModifier::End => { + map.serialize_entry("startOrEnd", &Some("end"))?; + map.serialize_entry("timestamp", &None::)?; + } + AtModifier::At(time) => { + map.serialize_entry("startOrEnd", &None::<&str>)?; + map.serialize_entry( + "timestamp", + &time + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or(Duration::ZERO) + .as_millis(), + )?; + } + } + + map.end() + } +} + impl TryFrom for AtModifier { type Error = String; @@ -339,6 +456,7 @@ impl fmt::Display for EvalStmt { /// /// parameter is only required for `count_values`, `quantile`, `topk` and `bottomk`. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ser", derive(serde::Serialize))] pub struct AggregateExpr { /// The used aggregation operation. pub op: TokenType, @@ -413,6 +531,21 @@ impl Prettier for UnaryExpr { } } +#[cfg(feature = "ser")] +impl serde::Serialize for UnaryExpr { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(Some(2))?; + map.serialize_entry("op", "-")?; + map.serialize_entry("expr", &self.expr)?; + + map.end() + } +} + /// Grammar: /// ``` norust /// ignoring(