Skip to main content

shape_ast/parser/queries/
alert.rs

1//! Alert query parsing
2
3use crate::error::{Result, ShapeError};
4use crate::parser::pair_location;
5use pest::iterators::Pair;
6
7use crate::ast::AlertQuery;
8use crate::parser::string_literals::parse_string_literal;
9use crate::parser::{Rule, expressions};
10
11/// Parse an alert query
12///
13/// Syntax: `alert when <condition> [message <string>] [webhook <url>]`
14pub fn parse_alert_query(pair: Pair<Rule>) -> Result<AlertQuery> {
15    let pair_loc = pair_location(&pair);
16    let mut inner = pair.into_inner();
17
18    // Skip "alert" keyword
19    inner.next();
20    // Skip "when" keyword
21    inner.next();
22
23    // Parse condition (required)
24    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
25        message: "expected condition after 'alert when'".to_string(),
26        location: Some(
27            pair_loc
28                .clone()
29                .with_hint("provide a boolean condition, e.g., alert when rsi(14) > 70"),
30        ),
31    })?;
32    let condition = expressions::parse_expression(condition_pair)?;
33
34    let mut message = None;
35    let mut webhook = None;
36
37    // Parse optional alert options
38    if let Some(options) = inner.next() {
39        let mut opt_inner = options.into_inner();
40        if let Some(msg_pair) = opt_inner.next() {
41            if msg_pair.as_rule() == Rule::string {
42                message = Some(parse_string_literal(msg_pair.as_str())?);
43            }
44        }
45        if let Some(webhook_pair) = opt_inner.next() {
46            if webhook_pair.as_rule() == Rule::string {
47                webhook = Some(parse_string_literal(webhook_pair.as_str())?);
48            }
49        }
50    }
51
52    Ok(AlertQuery {
53        condition,
54        message,
55        webhook,
56    })
57}