use std::borrow::Cow;
use std::collections::BTreeMap;
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
use serde::{Deserialize, Serialize};
use crate::primitives::{Identifier, RefPath};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum Expr {
Lit(LitExpr),
Ref(RefExpr),
Fn(FnExpr),
}
impl Expr {
pub fn lit(value: impl Into<serde_json::Value>) -> Self {
Expr::Lit(LitExpr { lit: value.into() })
}
pub fn reference(path: RefPath) -> Self {
Expr::Ref(RefExpr { r#ref: path })
}
pub fn call(f: PureFn, args: Vec<Expr>) -> Self {
Expr::Fn(FnExpr { r#fn: f, args })
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct LitExpr {
pub lit: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RefExpr {
pub r#ref: RefPath,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(extend("allOf" = [
{ "if": { "properties": { "fn": { "enum": ["eq", "ne", "jsonPath"] } } },
"then": { "properties": { "args": { "minItems": 2, "maxItems": 2 } } } },
{ "if": { "properties": { "fn": { "enum": ["not", "len"] } } },
"then": { "properties": { "args": { "minItems": 1, "maxItems": 1 } } } },
{ "if": { "properties": { "fn": { "enum": ["and", "or", "coalesce"] } } },
"then": { "properties": { "args": { "minItems": 2 } } } },
{ "if": { "properties": { "fn": { "const": "concat" } } },
"then": { "properties": { "args": { "minItems": 1 } } } },
{ "if": { "properties": { "fn": { "const": "regexMatch" } } },
"then": { "properties": { "args": { "minItems": 2, "maxItems": 3 } } } }
]))]
pub struct FnExpr {
pub r#fn: PureFn,
pub args: Vec<Expr>,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
)]
#[serde(rename_all = "camelCase")]
pub enum PureFn {
Eq,
Ne,
Not,
And,
Or,
Concat,
Len,
Coalesce,
JsonPath,
RegexMatch,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ExprMap(pub BTreeMap<Identifier, Expr>);
impl ExprMap {
pub fn new() -> Self {
Self::default()
}
}
impl std::ops::Deref for ExprMap {
type Target = BTreeMap<Identifier, Expr>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for ExprMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<BTreeMap<Identifier, Expr>> for ExprMap {
fn from(map: BTreeMap<Identifier, Expr>) -> Self {
Self(map)
}
}
impl FromIterator<(Identifier, Expr)> for ExprMap {
fn from_iter<I: IntoIterator<Item = (Identifier, Expr)>>(iter: I) -> Self {
Self(iter.into_iter().collect())
}
}
impl JsonSchema for ExprMap {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("ExprMap")
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("pointlock_ir::ExprMap")
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "object",
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
"additionalProperties": generator.subschema_for::<Expr>(),
"description": "Identifier-keyed map of expressions (exemption class 2: keys are data, constrained by propertyNames)."
})
}
}