Trim comments across Rust and webapp source to direct one-liners (ticket 030)
Pass over src/*.rs, webapp/src/**, and the in-app "?" docs prose: condensed multi-paragraph doc comments to single lines that state the non-obvious why, deleted comments that only restated adjacent code, and tightened the Docs.tsx help-panel wording. No logic, structure, or code formatting changed — verified via cargo test (143 passed) and tsc -b && vite build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3cef6dc8ae
commit
00c21c8032
+11
-65
@@ -1,47 +1,5 @@
|
||||
//! Hand-rolled parser for a small PromQL subset (ticket 013).
|
||||
//!
|
||||
//! Design decision: a crates.io PromQL parser (e.g. `promql-parser`) was considered
|
||||
//! but rejected — the required subset here is tiny (metric name + label matchers,
|
||||
//! plus an optional `[5m]` range and a single top-level `sum()`/`avg()`/`rate()`
|
||||
//! wrapper), while a full PromQL parser brings a large, largely-unused AST plus an
|
||||
//! extra maintenance dependency. Mirroring `filter_parser.rs`'s approach (a small,
|
||||
//! engine-independent parser module) is simpler and easier to control/extend, and
|
||||
//! keeps parsing fully decoupled from the engine's index — this module knows nothing
|
||||
//! about `IndexKey`/`DataValue`, only `PromqlQuery`'s own types below.
|
||||
//!
|
||||
//! ## Supported grammar
|
||||
//!
|
||||
//! ```text
|
||||
//! query := aggregation | vector_selector
|
||||
//! aggregation := agg_op "(" vector_selector ")"
|
||||
//! agg_op := "sum" | "avg" | "rate" | "histogram" | "gauge"
|
||||
//! vector_selector := metric_name label_matchers? range_selector?
|
||||
//! metric_name := [a-zA-Z_][a-zA-Z0-9_]*
|
||||
//! label_matchers := "{" label_matcher ("," label_matcher)* "}"
|
||||
//! label_matcher := label_name "=" quoted_string
|
||||
//! label_name := [a-zA-Z_][a-zA-Z0-9_]*
|
||||
//! quoted_string := '"' (any char except '"' or '\')* '"'
|
||||
//! range_selector := "[" digits duration_unit? "]"
|
||||
//! duration_unit := "s" | "m" | "h" | "d"
|
||||
//! ```
|
||||
//!
|
||||
//! Only exact-match label matchers (`=`) are supported — PromQL's `!=`, `=~`, `!~`
|
||||
//! are out of scope for this ticket's required subset. `rate()` requires its inner
|
||||
//! vector selector to carry a range selector with a duration unit (matching real
|
||||
//! PromQL), and is rejected at parse time otherwise.
|
||||
//!
|
||||
//! Ticket 022 (metrics-as-counters rethink) adds two derived forms on top of the
|
||||
//! same grammar, both consumed as regular `PromqlSeriesDto` series like every
|
||||
//! other query shape:
|
||||
//! - `histogram(metric_name[width])`: buckets every matching counter event's
|
||||
//! (coerced-to-f64) value into fixed-width buckets of size `width` (a bare
|
||||
//! number, no duration unit — reusing the `[...]` range-selector slot for a
|
||||
//! different purpose than `rate()`'s duration), and emits one series per
|
||||
//! bucket, labeled `le` (Prometheus's "less-than-or-equal" convention) with
|
||||
//! that bucket's upper bound, holding a single `(timestamp_ms, count)` point.
|
||||
//! - `gauge(metric_name)`: takes no range selector; emits the most-recent
|
||||
//! matching counter event's value per label combination as a single point,
|
||||
//! i.e. the counter's series collapsed down to its last value.
|
||||
//! Hand-rolled parser for a small PromQL subset: metric name + label matchers, an optional `[5m]` range, and a top-level `sum()`/`avg()`/`rate()`/`histogram()`/`gauge()` wrapper.
|
||||
//! Only exact-match (`=`) label matchers are supported; `rate()` requires a duration range selector, while `histogram(metric[width])` reuses that `[...]` slot for a bare bucket width instead.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -50,10 +8,9 @@ pub enum AggOp {
|
||||
Sum,
|
||||
Avg,
|
||||
Rate,
|
||||
/// Ticket 022: buckets counter values into fixed-width buckets. Requires
|
||||
/// `bucket_width`.
|
||||
/// Buckets counter values into fixed-width buckets; requires `bucket_width`.
|
||||
Histogram,
|
||||
/// Ticket 022: the most-recent counter value per label combination.
|
||||
/// The most-recent counter value per label combination.
|
||||
Gauge,
|
||||
}
|
||||
|
||||
@@ -68,23 +25,19 @@ pub struct PromqlQuery {
|
||||
pub agg: Option<AggOp>,
|
||||
pub metric_name: String,
|
||||
pub matchers: Vec<LabelMatcher>,
|
||||
/// Duration range selector, e.g. `[5m]`. Only meaningful for `rate()`.
|
||||
/// Duration range selector, e.g. `[5m]`; only meaningful for `rate()`.
|
||||
pub range: Option<Duration>,
|
||||
/// Bare-number range selector, e.g. `[10]`. Only meaningful for
|
||||
/// `histogram()`, where it's the bucket width.
|
||||
/// Bare-number range selector, e.g. `[10]`; only meaningful for `histogram()`, where it's the bucket width.
|
||||
pub bucket_width: Option<f64>,
|
||||
}
|
||||
|
||||
/// What a `[...]` range selector turned out to hold, disambiguated by whether
|
||||
/// a duration unit followed the digits.
|
||||
/// What a `[...]` range selector turned out to hold: a duration, or a bare width.
|
||||
enum RangeSelector {
|
||||
Duration(Duration),
|
||||
BareWidth(f64),
|
||||
}
|
||||
|
||||
/// Parses a PromQL query string per the grammar documented above. Returns a
|
||||
/// human-readable error message (suitable for surfacing directly to an API caller)
|
||||
/// on any malformed input.
|
||||
/// Parses a PromQL query string, returning a human-readable error message on malformed input.
|
||||
pub fn parse_query(input: &str) -> Result<PromqlQuery, String> {
|
||||
let mut parser = Parser::new(input);
|
||||
let query = parser.parse_query()?;
|
||||
@@ -143,7 +96,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `[a-zA-Z_][a-zA-Z0-9_]*`, used for both metric names and label names.
|
||||
fn parse_identifier(&mut self) -> Result<String, String> {
|
||||
self.skip_whitespace();
|
||||
let rest = self.remaining();
|
||||
@@ -214,9 +166,7 @@ impl<'a> Parser<'a> {
|
||||
Ok(matchers)
|
||||
}
|
||||
|
||||
/// `[<digits><unit>]` (e.g. `[5m]`, a duration — used by `rate()`) or a
|
||||
/// bare `[<digits>]` with no unit (a plain number — used by `histogram()`
|
||||
/// as a bucket width).
|
||||
/// Parses `[<digits><unit>]` (a duration, e.g. `[5m]`) or a bare `[<digits>]` (a bucket width).
|
||||
fn parse_range_selector(&mut self) -> Result<Option<RangeSelector>, String> {
|
||||
self.skip_whitespace();
|
||||
if self.peek() != Some('[') {
|
||||
@@ -290,10 +240,7 @@ impl<'a> Parser<'a> {
|
||||
inner.agg = agg;
|
||||
Ok(inner)
|
||||
} else {
|
||||
// Not (or not followed by) an aggregation call — rewind and parse the
|
||||
// whole thing as a plain vector selector (covers metric names that
|
||||
// happen to collide with an agg_op keyword, e.g. a metric named `rate`
|
||||
// used without a call).
|
||||
// Rewind and parse as a plain vector selector, covering metric names that collide with an agg_op keyword.
|
||||
self.pos = checkpoint;
|
||||
self.parse_vector_selector()
|
||||
}
|
||||
@@ -387,8 +334,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rejects_unknown_aggregation() {
|
||||
// `count` isn't a supported agg_op, so this parses as a bare metric named
|
||||
// `count`, then trips on the unexpected trailing `(...)`.
|
||||
// `count` isn't a supported agg_op, so this parses as a bare metric name and trips on the trailing `(...)`.
|
||||
let err = parse_query("count(requests)").unwrap_err();
|
||||
assert!(err.contains("unexpected trailing input"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user