Skip to main content

dol/
ast.rs

1//! Abstract syntax tree (AST) types for parsed DOL queries.
2//!
3//! Defines all node types produced by the parser: [`Query`] (top-level),
4//! [`Expression`] (conditions), [`PipelineNode`] (transformations), and
5//! their supporting enums/structs. All types are [`Serialize`] for
6//! debugging and analysis.
7//!
8//! # Example
9//!
10//! ```ignore
11//! use dol::ast::*;
12//! let query = Query::Ping;
13//! ```
14
15use serde::Serialize;
16
17#[derive(Debug, Clone, PartialEq, Serialize)]
18pub enum Query {
19    Observe(ObserveQuery),
20    Events(EventsQuery),
21    Inspect(InspectQuery),
22    Analyze(AnalyzeQuery),
23    Alert(AlertRule),
24    Fields(CollectionTarget),
25    Logs(LogsQuery),
26    Compose(ComposeQuery),
27    Ping,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize)]
31pub struct ObserveQuery {
32    pub target: CollectionTarget,
33    pub time: Option<TimeSelector>,
34    pub filter: Option<Expression>,
35    pub join: Option<JoinClause>,
36    pub pipeline: Vec<PipelineNode>,
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize)]
40pub struct JoinClause {
41    pub right: CollectionTarget,
42    pub left_key: Expression,
43    pub right_key: Expression,
44}
45
46#[derive(Debug, Clone, PartialEq, Serialize)]
47pub struct EventsQuery {
48    pub target: CollectionTarget,
49    pub time: Option<TimeSelector>,
50    pub filter: Option<Expression>,
51    pub pipeline: Vec<PipelineNode>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
55pub struct InspectQuery {
56    pub target: SingularTarget,
57    pub at: Option<String>,
58}
59
60#[derive(Debug, Clone, PartialEq, Serialize)]
61pub struct ComposeQuery {
62    pub project: String,
63    pub target: ComposeTarget,
64    pub pipeline: Vec<PipelineNode>,
65    pub service: Option<String>,
66    pub port_number: Option<u64>,
67    pub tail: Option<u64>,
68    pub config_target: Option<ConfigTarget>,
69}
70
71#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
72pub enum ComposeTarget {
73    Containers,
74    Services,
75    Networks,
76    Volumes,
77    Health,
78    Projects,
79    Images,
80    Stats,
81    Ps,
82    Events,
83    Port,
84    Config,
85    Logs,
86}
87
88#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
89pub enum ConfigTarget {
90    All,
91    Services,
92    Networks,
93    Volumes,
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize)]
97pub struct LogsQuery {
98    pub container: String,
99    pub tail: Option<u64>,
100    pub filter: Option<Expression>,
101    pub pipeline: Vec<PipelineNode>,
102}
103
104#[derive(Debug, Clone, PartialEq, Serialize)]
105pub struct AnalyzeQuery {
106    pub target: AnalysisTarget,
107    pub verb: AnalysisVerb,
108    pub subject: Option<String>,
109    pub time: Option<TimeSelector>,
110    pub pipeline: Vec<PipelineNode>,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize)]
114pub struct AlertRule {
115    pub condition: Expression,
116    pub duration: Option<Duration>,
117    pub action: AlertAction,
118}
119
120#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
121pub enum CollectionTarget {
122    Containers,
123    Images,
124    Networks,
125    Volumes,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
129pub struct SingularTarget {
130    pub kind: SingularTargetKind,
131    pub value: String,
132}
133
134#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
135pub enum SingularTargetKind {
136    Container,
137    Image,
138    Network,
139    Volume,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
143pub enum AnalysisTarget {
144    Collection(CollectionTarget),
145    Singular(SingularTarget),
146}
147
148#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
149pub enum AnalysisVerb {
150    Find,
151    Correlate,
152    Explain,
153}
154
155#[derive(Debug, Clone, PartialEq, Serialize)]
156pub enum PipelineNode {
157    Where(Expression),
158    Select(Vec<String>),
159    GroupBy {
160        fields: Vec<String>,
161        aggregates: Vec<AggregateExpr>,
162    },
163    Having(Expression),
164    SortBy {
165        fields: Vec<(String, SortDirection)>,
166    },
167    Limit(u64),
168    Offset(u64),
169    Distinct,
170    Alert(String),
171    If {
172        condition: Expression,
173        then_branch: Vec<Self>,
174        else_branch: Option<Vec<Self>>,
175    },
176    Set {
177        field: String,
178        value: SetValue,
179    },
180    Fill {
181        field: String,
182        /// The default value expression (supports if/case/when like `set`).
183        default: SetValue,
184        /// Optional condition: only fill rows that match this condition.
185        condition: Option<Expression>,
186    },
187    Let {
188        name: String,
189        value: Expression,
190    },
191    /// Print debug info (row count and schema) to stderr.
192    /// Does not modify the data stream.
193    Debug,
194    /// Assert that a condition holds for every row.
195    /// If any row fails the condition, the query fails with an error.
196    /// Acts as a pass-through when all rows pass.
197    Assert(Expression),
198    /// Assign a sequential row number (1-based) to each row.
199    RowNumber {
200        /// The alias/column name for the row number.
201        alias: String,
202    },
203    /// Rank rows by a field value (ties get same rank).
204    Rank {
205        /// Field to rank by.
206        field: String,
207        /// The alias/column name for the rank.
208        alias: String,
209    },
210    /// Access the value of a field from the previous row (like SQL LAG).
211    Lag {
212        /// Field whose value from the previous row to retrieve.
213        field: String,
214        /// The alias/column name for the lag value.
215        alias: String,
216        /// How many rows to look back (default 1).
217        offset: u64,
218    },
219    /// Access the value of a field from the next row (like SQL LEAD).
220    Lead {
221        /// Field whose value from the next row to retrieve.
222        field: String,
223        /// The alias/column name for the lead value.
224        alias: String,
225        /// How many rows to look ahead (default 1).
226        offset: u64,
227    },
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
231pub struct AggregateExpr {
232    pub function: String,
233    pub field: String,
234    pub alias: String,
235    /// Additional arguments for aggregate functions (e.g., percentile value for `percentile`).
236    pub args: Vec<String>,
237}
238
239#[derive(Debug, Clone, PartialEq, Serialize)]
240pub enum SetValue {
241    Literal(Value),
242    Expr(Expression),
243    Case {
244        when_clauses: Vec<(Expression, Expression)>,
245        else_value: Option<Expression>,
246    },
247    IfElse {
248        condition: Expression,
249        then_value: Expression,
250        else_value: Option<Expression>,
251    },
252}
253
254#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
255pub enum SortDirection {
256    Asc,
257    Desc,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
261pub enum TimeSelector {
262    Last(Duration),
263    Range { from: String, to: String },
264}
265
266#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
267pub struct Duration {
268    pub value: u64,
269    pub unit: DurationUnit,
270}
271
272#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
273pub enum DurationUnit {
274    Seconds,
275    Minutes,
276    Hours,
277    Days,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
281pub enum AlertAction {
282    Print(String),
283    Webhook(String),
284    Restart(SingularTarget),
285    /// Send a formatted message to a Slack channel via Incoming Webhook.
286    Slack(String),
287    /// Send a formatted message to a Discord channel via Webhook.
288    Discord(String),
289    /// Send an email notification via SMTP.
290    Email {
291        /// Recipient email address.
292        to: String,
293        /// Email subject line.
294        subject: String,
295    },
296}
297
298#[derive(Debug, Clone, PartialEq, Serialize)]
299pub enum Expression {
300    Field(String),
301    Literal(Value),
302    Arithmetic {
303        left: Box<Self>,
304        op: BinOp,
305        right: Box<Self>,
306    },
307    Comparison {
308        left: Box<Self>,
309        operator: Operator,
310        right: Box<Self>,
311    },
312    In {
313        expr: Box<Self>,
314        values: Vec<Value>,
315    },
316    Between {
317        expr: Box<Self>,
318        low: Box<Self>,
319        high: Box<Self>,
320    },
321    IsNull(Box<Self>),
322    IsNotNull(Box<Self>),
323    FnCall {
324        name: String,
325        args: Vec<Self>,
326    },
327    And(Box<Self>, Box<Self>),
328    Or(Box<Self>, Box<Self>),
329    Not(Box<Self>),
330}
331
332#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
333pub enum BinOp {
334    Add,
335    Sub,
336    Mul,
337    Div,
338    Mod,
339}
340
341#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
342pub enum Operator {
343    Eq,
344    NotEq,
345    Gt,
346    Lt,
347    Gte,
348    Lte,
349    Contains,
350    Matches,
351    StartsWith,
352    EndsWith,
353}
354
355#[derive(Debug, Clone, PartialEq, Serialize)]
356pub enum Value {
357    String(String),
358    Identifier(String),
359    Integer(i64),
360    Float(f64),
361    Percentage(f64),
362    Boolean(bool),
363}