1use std::borrow::Cow;
10use std::collections::BTreeMap;
11
12use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
13use serde::{Deserialize, Serialize};
14
15use crate::primitives::{Identifier, RefPath};
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
23#[serde(untagged)]
24pub enum Expr {
25 Lit(LitExpr),
27 Ref(RefExpr),
29 Fn(FnExpr),
31}
32
33impl Expr {
34 pub fn lit(value: impl Into<serde_json::Value>) -> Self {
36 Expr::Lit(LitExpr { lit: value.into() })
37 }
38
39 pub fn reference(path: RefPath) -> Self {
41 Expr::Ref(RefExpr { r#ref: path })
42 }
43
44 pub fn call(f: PureFn, args: Vec<Expr>) -> Self {
47 Expr::Fn(FnExpr { r#fn: f, args })
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
53#[serde(deny_unknown_fields)]
54pub struct LitExpr {
55 pub lit: serde_json::Value,
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
61#[serde(deny_unknown_fields)]
62pub struct RefExpr {
63 pub r#ref: RefPath,
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
74#[serde(deny_unknown_fields)]
75#[schemars(extend("allOf" = [
76 { "if": { "properties": { "fn": { "enum": ["eq", "ne", "jsonPath"] } } },
77 "then": { "properties": { "args": { "minItems": 2, "maxItems": 2 } } } },
78 { "if": { "properties": { "fn": { "enum": ["not", "len"] } } },
79 "then": { "properties": { "args": { "minItems": 1, "maxItems": 1 } } } },
80 { "if": { "properties": { "fn": { "enum": ["and", "or", "coalesce"] } } },
81 "then": { "properties": { "args": { "minItems": 2 } } } },
82 { "if": { "properties": { "fn": { "const": "concat" } } },
83 "then": { "properties": { "args": { "minItems": 1 } } } },
84 { "if": { "properties": { "fn": { "const": "regexMatch" } } },
85 "then": { "properties": { "args": { "minItems": 2, "maxItems": 3 } } } }
86]))]
87pub struct FnExpr {
88 pub r#fn: PureFn,
90 pub args: Vec<Expr>,
92}
93
94#[derive(
96 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
97)]
98#[serde(rename_all = "camelCase")]
99pub enum PureFn {
100 Eq,
102 Ne,
104 Not,
106 And,
108 Or,
110 Concat,
112 Len,
114 Coalesce,
116 JsonPath,
118 RegexMatch,
120}
121
122#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
129#[serde(transparent)]
130pub struct ExprMap(pub BTreeMap<Identifier, Expr>);
131
132impl ExprMap {
133 pub fn new() -> Self {
135 Self::default()
136 }
137}
138
139impl std::ops::Deref for ExprMap {
140 type Target = BTreeMap<Identifier, Expr>;
141 fn deref(&self) -> &Self::Target {
142 &self.0
143 }
144}
145
146impl std::ops::DerefMut for ExprMap {
147 fn deref_mut(&mut self) -> &mut Self::Target {
148 &mut self.0
149 }
150}
151
152impl From<BTreeMap<Identifier, Expr>> for ExprMap {
153 fn from(map: BTreeMap<Identifier, Expr>) -> Self {
154 Self(map)
155 }
156}
157
158impl FromIterator<(Identifier, Expr)> for ExprMap {
159 fn from_iter<I: IntoIterator<Item = (Identifier, Expr)>>(iter: I) -> Self {
160 Self(iter.into_iter().collect())
161 }
162}
163
164impl JsonSchema for ExprMap {
165 fn schema_name() -> Cow<'static, str> {
166 Cow::Borrowed("ExprMap")
167 }
168 fn schema_id() -> Cow<'static, str> {
169 Cow::Borrowed("pointlock_ir::ExprMap")
170 }
171 fn json_schema(generator: &mut SchemaGenerator) -> Schema {
172 json_schema!({
173 "type": "object",
174 "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
175 "additionalProperties": generator.subschema_for::<Expr>(),
176 "description": "Identifier-keyed map of expressions (exemption class 2: keys are data, constrained by propertyNames)."
177 })
178 }
179}