use crate::types::FieldType;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Script {
pub label: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default)]
pub parameters: HashMap<String, ParameterDefinition>,
pub functions: Vec<Function>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(skip_serializing, skip_deserializing)]
pub created_at: Option<DateTime<Utc>>,
#[serde(skip_serializing, skip_deserializing)]
pub updated_at: Option<DateTime<Utc>>,
}
impl Script {
pub fn new(label: impl Into<String>, name: impl Into<String>) -> Self {
Self {
label: label.into(),
name: name.into(),
description: None,
version: None,
parameters: HashMap::new(),
functions: Vec::new(),
tags: Vec::new(),
created_at: None, updated_at: None, }
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_version(mut self, version: impl Into<String>) -> Self {
self.version = Some(version.into());
self
}
pub fn with_parameter(mut self, param: ParameterDefinition) -> Self {
self.parameters.insert(param.name.clone(), param);
self
}
pub fn with_function(mut self, function: Function) -> Self {
self.functions.push(function);
self
}
pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.tags.push(tag.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserFunction {
pub id: Option<String>,
pub label: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default)]
pub parameters: HashMap<String, ParameterDefinition>,
pub functions: Vec<Function>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(skip_serializing, skip_deserializing)]
pub created_at: Option<DateTime<Utc>>,
#[serde(skip_serializing, skip_deserializing)]
pub updated_at: Option<DateTime<Utc>>,
}
impl UserFunction {
pub fn new(label: impl Into<String>, name: impl Into<String>) -> Self {
Self {
id: None,
label: label.into(),
name: name.into(),
description: None,
version: None,
parameters: HashMap::new(),
functions: Vec::new(),
tags: Vec::new(),
created_at: None,
updated_at: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_version(mut self, version: impl Into<String>) -> Self {
self.version = Some(version.into());
self
}
pub fn with_parameter(mut self, param: ParameterDefinition) -> Self {
self.parameters.insert(param.name.clone(), param);
self
}
pub fn with_function(mut self, function: Function) -> Self {
self.functions.push(function);
self
}
pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.tags.push(tag.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterDefinition {
#[serde(skip_serializing, default)]
pub name: String,
#[serde(default)]
pub required: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<FieldType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
impl ParameterDefinition {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
required: false,
default: None,
description: None,
}
}
pub fn required(mut self) -> Self {
self.required = true;
self
}
pub fn with_default(mut self, default: FieldType) -> Self {
self.default = Some(default);
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum ScriptCondition {
FieldEquals {
field: String,
value: serde_json::Value,
},
FieldExists { field: String },
HasRecords,
CountEquals { count: usize },
CountGreaterThan { count: usize },
CountLessThan { count: usize },
And { conditions: Vec<ScriptCondition> },
Or { conditions: Vec<ScriptCondition> },
Not { condition: Box<ScriptCondition> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "PascalCase")]
pub enum Function {
FindAll { collection: String },
Query {
collection: String,
#[serde(skip_serializing_if = "Option::is_none")]
filter: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
sort: Option<Vec<SortFieldConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
skip: Option<serde_json::Value>,
},
Project { fields: Vec<String>, exclude: bool },
Group {
by_fields: Vec<String>,
functions: Vec<GroupFunctionConfig>,
},
Count { output_field: String },
FindById {
collection: String,
record_id: String,
},
FindOne {
collection: String,
key: String,
value: serde_json::Value,
},
Insert {
collection: String,
record: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
ttl: Option<serde_json::Value>,
},
Update {
collection: String,
filter: serde_json::Value,
updates: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
ttl: Option<serde_json::Value>,
},
UpdateById {
collection: String,
record_id: String,
updates: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
ttl: Option<serde_json::Value>,
},
FindOneAndUpdate {
collection: String,
filter: serde_json::Value,
updates: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
ttl: Option<serde_json::Value>,
},
UpdateWithAction {
collection: String,
filter: serde_json::Value,
actions: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
},
Delete {
collection: String,
filter: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
},
DeleteById {
collection: String,
record_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
},
BatchInsert {
collection: String,
records: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
bypass_ripple: Option<bool>,
},
BatchDelete {
ids: serde_json::Value,
#[serde(default)]
bypass_ripple: bool,
},
HttpRequest {
url: String,
#[serde(default = "default_method")]
method: String,
#[serde(skip_serializing_if = "Option::is_none")]
headers: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
body: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
timeout_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
output_field: Option<String>,
},
VectorSearch {
query_vector: Vec<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<serde_json::Value>,
},
TextSearch {
collection: String,
query_text: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
fields: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
fuzzy: Option<bool>,
},
HybridSearch {
text_query: String,
vector_query: Vec<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<serde_json::Value>,
},
Chat {
messages: Vec<ChatMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
max_tokens: Option<i32>,
},
Embed {
input_field: String,
output_field: String,
#[serde(skip_serializing_if = "Option::is_none")]
model: Option<String>,
},
If {
condition: ScriptCondition,
then_functions: Vec<Box<Function>>,
#[serde(skip_serializing_if = "Option::is_none")]
else_functions: Option<Vec<Box<Function>>>,
},
ForEach { functions: Vec<Box<Function>> },
CallFunction {
function_label: String,
params: Option<HashMap<String, serde_json::Value>>,
},
CreateSavepoint { name: String },
RollbackToSavepoint { name: String },
ReleaseSavepoint { name: String },
KvGet { key: serde_json::Value },
KvSet {
key: serde_json::Value,
value: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
ttl: Option<serde_json::Value>,
},
KvDelete { key: serde_json::Value },
KvExists {
key: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
output_field: Option<String>,
},
KvQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pattern: Option<serde_json::Value>,
#[serde(default)]
include_expired: bool,
},
SWR {
cache_key: String,
ttl: serde_json::Value,
url: String,
method: String,
#[serde(skip_serializing_if = "Option::is_none")]
headers: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
body: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
timeout_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
output_field: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
collection: Option<String>,
},
}
fn default_method() -> String {
"GET".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: String,
pub content: String,
}
impl ChatMessage {
pub fn system(content: impl Into<String>) -> Self {
Self {
role: "system".to_string(),
content: content.into(),
}
}
pub fn user(content: impl Into<String>) -> Self {
Self {
role: "user".to_string(),
content: content.into(),
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: "assistant".to_string(),
content: content.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupFunctionConfig {
pub output_field: String,
pub operation: GroupFunctionOp,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_field: Option<String>,
}
impl GroupFunctionConfig {
pub fn new(output_field: impl Into<String>, operation: GroupFunctionOp) -> Self {
Self {
output_field: output_field.into(),
operation,
input_field: None,
}
}
pub fn with_input_field(mut self, field: impl Into<String>) -> Self {
self.input_field = Some(field.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum GroupFunctionOp {
Sum,
Average,
Count,
Min,
Max,
First,
Last,
Push,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortFieldConfig {
pub field: String,
#[serde(default = "default_ascending")]
pub ascending: bool,
}
impl SortFieldConfig {
pub fn new(field: impl Into<String>) -> Self {
Self {
field: field.into(),
ascending: true,
}
}
pub fn descending(mut self) -> Self {
self.ascending = false;
self
}
pub fn ascending(mut self) -> Self {
self.ascending = true;
self
}
}
fn default_ascending() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionResult {
pub records: Vec<crate::Record>,
pub stats: FunctionStats,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionStats {
pub input_count: usize,
pub output_count: usize,
pub execution_time_ms: u128,
pub stages_executed: usize,
pub stage_stats: Vec<StageStats>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StageStats {
pub stage: String,
pub input_count: usize,
pub output_count: usize,
pub execution_time_ms: u128,
}