use crate::model::Expr;
use serde_json::Value as JsonValue;
#[derive(Debug, Clone, PartialEq)]
pub enum V2Expr {
Pipe(V2Pipe),
V1Fallback(Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2Pipe {
pub start: V2Start,
pub steps: Vec<V2Step>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum V2Start {
Ref(V2Ref),
PipeValue,
ImplicitPipeValue,
Literal(JsonValue),
V1Expr(Box<Expr>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum V2Ref {
Input(String), Context(String), Out(String), Pipe(String), Item(String), Acc(String), Local(String), }
#[derive(Debug, Clone, PartialEq)]
pub enum V2Step {
Op(V2OpStep),
Object(V2ObjectStep),
CustomCall(V2CustomCallStep),
Let(V2LetStep),
If(V2IfStep),
Map(V2MapStep),
Ref(V2Ref),
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2OpStep {
pub op: String,
pub args: Vec<V2Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2ObjectStep {
pub fields: Vec<V2ObjectField>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2ObjectField {
pub key: String,
pub value: V2ObjectFieldValue,
}
#[derive(Debug, Clone, PartialEq)]
pub enum V2ObjectFieldValue {
Expr(V2Expr),
Value(JsonValue),
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2CustomCallStep {
pub op: String,
pub with: Option<Vec<(String, V2CallArg)>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum V2CallArg {
Expr(V2Expr),
Value(JsonValue),
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2LetStep {
pub bindings: Vec<(String, V2Expr)>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2IfStep {
pub cond: V2Condition,
pub then_branch: V2Pipe,
pub else_branch: Option<V2Pipe>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2MapStep {
pub steps: Vec<V2Step>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum V2Condition {
All(Vec<V2Condition>),
Any(Vec<V2Condition>),
Comparison(V2Comparison),
Expr(V2Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub struct V2Comparison {
pub op: V2ComparisonOp,
pub args: Vec<V2Expr>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum V2ComparisonOp {
Eq,
Ne,
Gt,
Gte,
Lt,
Lte,
Match,
}
pub(crate) fn object_field_rule_path(base_path: &str, key: &str) -> String {
if is_simple_object_field_key(key) {
format!("{}.object.{}", base_path, key)
} else {
let quoted = serde_json::to_string(key).unwrap_or_else(|_| "\"<invalid>\"".to_string());
format!("{}.object[{}]", base_path, quoted)
}
}
fn is_simple_object_field_key(key: &str) -> bool {
let mut chars = key.chars();
let Some(first) = chars.next() else {
return false;
};
(first.is_ascii_alphabetic() || first == '_')
&& chars.all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
}
#[cfg(test)]
#[path = "v2_model/tests.rs"]
mod v2_model_tests;