use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::errors::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct CalVersion(pub u32);
impl Default for CalVersion {
fn default() -> Self {
Self(1)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CalQuery {
#[serde(default)]
pub version: CalVersion,
pub statement: CalStatement,
pub pipeline: Vec<PipelineStage>,
pub with_options: Vec<WithOption>,
pub format: Option<FormatClause>,
pub let_bindings: Vec<LetBinding>,
#[serde(default)]
pub user_vars: HashMap<String, String>,
#[serde(skip)]
pub let_values: HashMap<String, Vec<String>>,
#[serde(skip)]
pub warnings: Vec<super::errors::CalWarning>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
#[allow(clippy::large_enum_variant)]
pub enum CalStatement {
#[serde(alias = "RECALL", alias = "Recall")]
Recall(RecallStmt),
#[serde(alias = "SET_OP", alias = "SetOp")]
SetOp(SetOpStmt),
#[serde(alias = "EXISTS", alias = "Exists")]
Exists(ExistsStmt),
#[serde(alias = "ASSEMBLE", alias = "Assemble")]
Assemble(AssembleStmt),
#[serde(alias = "HISTORY", alias = "History")]
History(HistoryStmt),
#[serde(alias = "EXPLAIN", alias = "Explain")]
Explain(ExplainStmt),
#[serde(alias = "DESCRIBE", alias = "Describe")]
Describe(DescribeStmt),
#[serde(alias = "BATCH", alias = "Batch")]
Batch(BatchStmt),
#[serde(alias = "COALESCE", alias = "Coalesce")]
Coalesce(CoalesceStmt),
#[serde(alias = "ADD", alias = "Add")]
Add(AddStmt),
#[serde(alias = "ADD_WORKFLOW", alias = "AddWorkflow")]
AddWorkflow(AddWorkflowStmt),
#[serde(alias = "SUPERSEDE", alias = "Supersede")]
Supersede(SupersedeStmt),
#[serde(alias = "SUPERSEDE_WORKFLOW", alias = "SupersedeWorkflow")]
SupersedeWorkflow(SupersedeWorkflowStmt),
#[serde(alias = "ACCUMULATE", alias = "Accumulate")]
Accumulate(AccumulateStmt),
#[serde(alias = "REVERT", alias = "Revert")]
Revert(RevertStmt),
ReportSubject(ReportSubjectStmt),
Forget(ForgetStmt),
Purge(PurgeStmt),
#[serde(alias = "GRANT")]
Grant(GrantStmt),
#[serde(alias = "REVOKE")]
Revoke(RevokeStmt),
#[serde(alias = "SHOW_GRANTS", alias = "ShowGrants")]
ShowGrants(ShowGrantsStmt),
#[serde(alias = "APPROVE")]
Approve(GovernanceStmt),
#[serde(alias = "REJECT")]
Reject(GovernanceStmt),
#[serde(alias = "APPLY")]
ApplyRec(GovernanceStmt),
#[serde(alias = "ROLLBACK")]
RollbackRec(GovernanceStmt),
#[serde(alias = "RUN_LOOP", alias = "RunLoop")]
RunLoop(RunLoopStmt),
#[serde(alias = "REMEMBER")]
Remember(RememberStmt),
#[serde(alias = "ENTITY_AT", alias = "EntityAt")]
EntityAt(EntityAtStmt),
#[serde(alias = "RUN_TRACE")]
RunTrace(RunTraceStmt),
#[serde(alias = "RUNS_TOUCHING")]
RunsTouching(RunsTouchingStmt),
#[serde(alias = "DERIVED_FROM")]
DerivedFrom(DerivedFromStmt),
#[serde(alias = "SHOW_FORKS")]
ShowForks(ShowForksStmt),
#[serde(alias = "MERGE")]
Merge(MergeStmt),
#[serde(alias = "RELATED")]
Related(RelatedStmt),
#[serde(alias = "NOVELTY")]
Novelty(NoveltyStmt),
DefineTemplate(DefineTemplateStmt),
DropTemplate(DropTemplateStmt),
DefineQuery(DefineQueryStmt),
DropQuery(DropQueryStmt),
RunQuery(RunQueryStmt),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RecallStmt {
pub grain_type: GrainTypePlural,
pub about: Option<AboutClause>,
pub where_clause: Option<WhereClause>,
pub recent: Option<RecentClause>,
pub since: Option<SinceClause>,
pub until: Option<UntilClause>,
pub like: Option<LikeClause>,
pub between: Option<BetweenClause>,
pub contradictions: Option<ContradictionsClause>,
pub limit: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub as_format: Option<FormatClause>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetOpStmt {
pub op: SetOp,
pub operands: Vec<CalStatement>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SetOp {
Union,
Intersect,
Except,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExistsStmt {
pub grain_type: GrainTypePlural,
pub where_clause: Option<WhereClause>,
pub about: Option<AboutClause>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AssembleStmt {
pub topic: String,
pub from: Source,
pub where_clause: Option<WhereClause>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sources: Option<Vec<NamedSource>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub budget: Option<BudgetSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<Vec<PrioritySpec>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<FormatClause>,
#[serde(skip_serializing_if = "Option::is_none")]
pub for_whom: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub assemble_with: Vec<AssembleWithOption>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub with_options: Vec<WithOption>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub streaming: bool,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NamedSource {
pub label: String,
pub query: Box<CalStatement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub literal: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub pinned: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub with_options: Vec<WithOption>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum BudgetUnit {
#[default]
Tokens,
Grains,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BudgetSpec {
pub tokens: u32,
#[serde(default)]
pub unit: BudgetUnit,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrioritySpec {
pub label: String,
pub weight: f64,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum AssembleWithOption {
Dedup { field: Option<String> },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Source {
Query(Box<RecallStmt>),
Parameter { name: String },
Hashes(Vec<String>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HistoryStmt {
pub hash: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub where_clause: Option<WhereClause>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diff_target: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExplainStmt {
pub inner: Box<CalStatement>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DescribeStmt {
pub target: DescribeTarget,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DescribeTarget {
GrainType(GrainTypePlural),
Schema,
Capabilities,
Server,
Fields(Option<GrainTypePlural>),
Templates,
Grammar,
Queries,
Query(String),
Principal(String),
Loop,
Analyzers,
Outcomes,
LoopPolicy,
Stats,
Integrity,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchEntry {
pub statement: CalStatement,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub pipeline: Vec<PipelineStage>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub with_options: Vec<super::ast::WithOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<FormatClause>,
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
pub user_vars: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchStmt {
pub statements: Vec<BatchEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
pub labeled: Option<Vec<(String, BatchEntry)>>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CoalesceStmt {
pub grain_type: GrainTypePlural,
pub where_clause: Option<WhereClause>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub branches: Vec<CoalesceBranch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub else_branch: Option<Box<CalStatement>>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CoalesceBranch {
pub query: CalStatement,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddStmt {
pub grain_type: GrainTypeSingular,
pub fields: Vec<FieldAssignment>,
pub reason: String,
#[serde(default)]
pub with_options: Vec<AddWithOption>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldAssignment {
pub field: String,
pub value: Value,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GraphEdge {
pub src: String,
pub dst: String,
pub cond: Option<String>,
pub repeat: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BindClause {
pub node: String,
pub hash: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddWorkflowStmt {
pub name: String,
pub nodes: Vec<String>,
pub edges: Vec<GraphEdge>,
pub bindings: Vec<BindClause>,
pub reason: String,
#[serde(default)]
pub with_options: Vec<AddWithOption>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupersedeWorkflowStmt {
pub hash: String,
pub nodes: Vec<String>,
pub edges: Vec<GraphEdge>,
pub bindings: Vec<BindClause>,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupersedeStmt {
pub hash: String,
pub set_clauses: Vec<FieldAssignment>,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AccumulateStmt {
pub grain_type: GrainTypeSingular,
pub target: AccumulateTarget,
pub add_ops: Vec<DeltaOp>,
pub set_ops: Vec<FieldAssignment>,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum AccumulateTarget {
TipResolved {
subject: String,
relation: String,
namespace: Option<String>,
},
Hash { hash: String },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeltaOp {
pub field: String,
pub delta: f64,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RevertStmt {
pub hash: String,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ForgetStmt {
pub target: ForgetTarget,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub text_mentions: bool,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ForgetTarget {
Hash { hash: String },
User { user_id: String },
Scope { scope: String },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PurgeStmt {
pub min_age_days: Option<f64>,
pub namespace: Option<String>,
pub limit: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grain_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReportSubjectStmt {
pub subject_id: String,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub text_mentions: bool,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EntityAtStmt {
pub subject: String,
pub relation: String,
pub at_ms: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub axis: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RunTraceStmt {
pub run_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RunsTouchingStmt {
pub hash: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depth: Option<usize>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DerivedFromStmt {
pub hash: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MergeStmt {
pub subject: String,
pub relation: String,
pub object: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidence: Option<f64>,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RelatedStmt {
pub start: String,
pub relations: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depth: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NoveltyStmt {
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ShowForksStmt {
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RememberStmt {
pub content: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub run_id: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GovernanceStmt {
pub hash: String,
pub reason: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RunLoopStmt {
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub full_sweep: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_new: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub if_stale_ms: Option<i64>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GrantStmt {
pub verbs: Vec<String>,
pub namespaces: Vec<String>,
pub principal: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RevokeStmt {
pub verbs: Vec<String>,
pub namespaces: Vec<String>,
pub principal: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ShowGrantsStmt {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub principal: Option<String>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DefineTemplateStmt {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub grain_types: Vec<String>,
pub source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sections: Option<TemplateSectionSources>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct TemplateSectionSources {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub header: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub element: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub element_summary: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub element_omit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_break: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub footer: Option<String>,
}
impl TemplateSectionSources {
pub fn is_empty(&self) -> bool {
self.header.is_none()
&& self.element.is_none()
&& self.element_summary.is_none()
&& self.element_omit.is_none()
&& self.source_break.is_none()
&& self.footer.is_none()
}
pub fn to_source(&self) -> String {
let mut out = String::new();
for (kw, body) in [
("HEADER", &self.header),
("ELEMENT", &self.element),
("ELEMENT_SUMMARY", &self.element_summary),
("ELEMENT_OMIT", &self.element_omit),
("SOURCE_BREAK", &self.source_break),
("FOOTER", &self.footer),
] {
if let Some(body) = body {
out.push_str(kw);
out.push_str(" {\n");
out.push_str(body);
out.push_str("\n}\n");
}
}
out
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DropTemplateStmt {
pub name: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct QueryParam {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DefineQueryStmt {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub params: Vec<QueryParam>,
pub body: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DropQueryStmt {
pub name: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RunQueryStmt {
pub name: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub bindings: Vec<(String, Value)>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WhereClause {
pub condition: Condition,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Condition {
Comparison {
field: String,
comparator: Comparator,
value: Value,
#[serde(skip)]
span: Option<Span>,
},
In {
field: String,
values: Vec<Value>,
#[serde(skip)]
span: Option<Span>,
},
NotIn {
field: String,
values: Vec<Value>,
#[serde(skip)]
span: Option<Span>,
},
IsNull {
field: String,
#[serde(skip)]
span: Option<Span>,
},
IsNotNull {
field: String,
#[serde(skip)]
span: Option<Span>,
},
Contains {
field: String,
value: String,
#[serde(skip)]
span: Option<Span>,
},
StartsWith {
field: String,
value: String,
#[serde(skip)]
span: Option<Span>,
},
And {
left: Box<Condition>,
right: Box<Condition>,
#[serde(skip)]
span: Option<Span>,
},
Or {
left: Box<Condition>,
right: Box<Condition>,
#[serde(skip)]
span: Option<Span>,
},
Not {
inner: Box<Condition>,
#[serde(skip)]
span: Option<Span>,
},
IsCategory {
field: String,
category: String,
#[serde(skip)]
span: Option<Span>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Comparator {
Eq,
NotEq,
Gte,
Lte,
Gt,
Lt,
}
impl std::fmt::Display for Comparator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Eq => write!(f, "="),
Self::NotEq => write!(f, "!="),
Self::Gte => write!(f, ">="),
Self::Lte => write!(f, "<="),
Self::Gt => write!(f, ">"),
Self::Lt => write!(f, "<"),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Value {
String { value: String },
Number { value: f64 },
Boolean { value: bool },
Array { values: Vec<Value> },
Hash { value: String },
Parameter { name: String },
}
impl Value {
pub fn type_name(&self) -> &'static str {
match self {
Self::String { .. } => "string",
Self::Number { .. } => "number",
Self::Boolean { .. } => "boolean",
Self::Array { .. } => "array",
Self::Hash { .. } => "hash",
Self::Parameter { .. } => "parameter",
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String { value } => write!(f, "\"{}\"", value),
Self::Number { value } => write!(f, "{}", value),
Self::Boolean { value } => write!(f, "{}", value),
Self::Array { values } => {
write!(f, "[")?;
for (i, v) in values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
Self::Hash { value } => write!(f, "#{}", value),
Self::Parameter { name } => write!(f, "${}", name),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "stage", rename_all = "snake_case")]
pub enum PipelineStage {
Select {
fields: Vec<String>,
#[serde(skip)]
span: Option<Span>,
},
OrderBy {
field: String,
descending: bool,
#[serde(skip)]
span: Option<Span>,
},
Limit {
value: u64,
#[serde(skip)]
span: Option<Span>,
},
Offset {
value: u64,
#[serde(skip)]
span: Option<Span>,
},
Count {
#[serde(skip)]
span: Option<Span>,
},
First {
#[serde(skip)]
span: Option<Span>,
},
Subjects {
#[serde(skip)]
span: Option<Span>,
},
Objects {
#[serde(skip)]
span: Option<Span>,
},
Hashes {
#[serde(skip)]
span: Option<Span>,
},
GroupBy {
field: String,
#[serde(skip)]
span: Option<Span>,
},
Project {
fields: Vec<ProjectField>,
#[serde(skip)]
span: Option<Span>,
},
Filter {
condition: Condition,
#[serde(skip)]
span: Option<Span>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProjectField {
pub field: String,
pub alias: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "option", rename_all = "snake_case")]
pub enum WithOption {
Superseded,
ScoreBreakdown,
Explanation,
Provenance,
ContradictionDetection,
Diversity { lambda: Option<f64> },
Dedup { field: Option<String> },
ProgressiveDisclosure { level: Option<String> },
Consistency { level: Option<String> },
Locale { tag: String },
Cache { ttl_seconds: u64 },
Rerank { model: Option<String> },
LlmRerank { model: Option<String> },
QueryExpansion,
QueryDecompose,
Hyde,
ConflictResolution,
IncludeSources,
AnnotateRelativeTime,
RecencyWeight { weight: f64 },
MinScore { score: f64 },
MultiHop { hops: u64 },
SessionAffinity { boost: f64 },
SubjectAffinity { boost: f64 },
SessionCoverage { min_per_ns: u64 },
MaxNamespaces { max: u64 },
Exhaustive { max_rounds: Option<u64> },
SessionCensus {
min_per_session: Option<u64>,
min_score: Option<f64>,
},
AggregationIntent,
PreferenceEnrichment,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "option", rename_all = "snake_case")]
pub enum AddWithOption {
ExtractEventDate,
AutoRelate,
ExtractMemories,
Sync,
Occurrence,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "format", rename_all = "snake_case")]
pub enum FormatSpec {
Sml,
Toon,
Markdown,
Json,
Yaml,
Text,
Triples,
Csv,
Table,
Preset {
name: String,
},
Template {
template: String,
},
TemplateRef {
name: String,
},
TemplateInline {
sections: TemplateSectionSources,
},
}
impl FormatSpec {
pub fn canonical_key(&self) -> &str {
match self {
Self::Json => "json",
Self::Markdown => "markdown",
Self::Yaml => "yaml",
Self::Text => "text",
Self::Sml => "sml",
Self::Toon => "toon",
Self::Triples => "triples",
Self::Csv => "csv",
Self::Table => "table",
Self::Preset { name } => name.as_str(),
Self::TemplateRef { name } => name.as_str(),
Self::Template { .. } | Self::TemplateInline { .. } => "template",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AliasedFormat {
pub spec: FormatSpec,
#[serde(skip_serializing_if = "Option::is_none")]
pub alias: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum FormatClause {
Single(FormatSpec),
Multi(Vec<AliasedFormat>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GrainTypePlural {
Facts,
Events,
States,
Workflows,
Tools,
Observations,
Goals,
Reasonings,
Consensuses,
Consents,
Skills,
Recommendations,
Triggers,
All,
}
impl GrainTypePlural {
pub fn parse(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"facts" | "fact" => Some(Self::Facts),
"events" | "event" => Some(Self::Events),
"states" | "state" => Some(Self::States),
"workflows" | "workflow" => Some(Self::Workflows),
"tools" | "tool" => Some(Self::Tools),
"observations" | "observation" => Some(Self::Observations),
"goals" | "goal" => Some(Self::Goals),
"reasonings" | "reasoning" => Some(Self::Reasonings),
"consensuses" | "consensus" => Some(Self::Consensuses),
"consents" | "consent" => Some(Self::Consents),
"skills" | "skill" => Some(Self::Skills),
"recommendations" | "recommendation" => Some(Self::Recommendations),
"triggers" | "trigger" => Some(Self::Triggers),
"*" | "grains" | "all" => Some(Self::All),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Facts => "facts",
Self::Events => "events",
Self::States => "states",
Self::Workflows => "workflows",
Self::Tools => "tools",
Self::Observations => "observations",
Self::Goals => "goals",
Self::Reasonings => "reasonings",
Self::Consensuses => "consensuses",
Self::Consents => "consents",
Self::Skills => "skills",
Self::Recommendations => "recommendations",
Self::Triggers => "triggers",
Self::All => "*",
}
}
pub fn to_grain_type(&self) -> Option<areev_core::types::GrainType> {
match self {
Self::Facts => Some(areev_core::types::GrainType::Fact),
Self::Events => Some(areev_core::types::GrainType::Event),
Self::States => Some(areev_core::types::GrainType::State),
Self::Workflows => Some(areev_core::types::GrainType::Workflow),
Self::Tools => Some(areev_core::types::GrainType::Tool),
Self::Observations => Some(areev_core::types::GrainType::Observation),
Self::Goals => Some(areev_core::types::GrainType::Goal),
Self::Reasonings => Some(areev_core::types::GrainType::Reasoning),
Self::Consensuses => Some(areev_core::types::GrainType::Consensus),
Self::Consents => Some(areev_core::types::GrainType::Consent),
Self::Skills => Some(areev_core::types::GrainType::Skill),
Self::Recommendations => Some(areev_core::types::GrainType::Recommendation),
Self::Triggers => Some(areev_core::types::GrainType::Trigger),
Self::All => None,
}
}
}
impl std::fmt::Display for GrainTypePlural {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GrainTypeSingular {
Fact,
Event,
State,
Workflow,
Tool,
Observation,
Goal,
Reasoning,
Consensus,
Consent,
Skill,
Recommendation,
}
impl GrainTypeSingular {
pub fn parse(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"fact" => Some(Self::Fact),
"event" => Some(Self::Event),
"state" => Some(Self::State),
"workflow" => Some(Self::Workflow),
"tool" => Some(Self::Tool),
"observation" => Some(Self::Observation),
"goal" => Some(Self::Goal),
"reasoning" => Some(Self::Reasoning),
"consensus" => Some(Self::Consensus),
"consent" => Some(Self::Consent),
"skill" => Some(Self::Skill),
"recommendation" => Some(Self::Recommendation),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Fact => "fact",
Self::Event => "event",
Self::State => "state",
Self::Workflow => "workflow",
Self::Tool => "tool",
Self::Observation => "observation",
Self::Goal => "goal",
Self::Reasoning => "reasoning",
Self::Consensus => "consensus",
Self::Consent => "consent",
Self::Skill => "skill",
Self::Recommendation => "recommendation",
}
}
pub fn to_grain_type(&self) -> areev_core::types::GrainType {
match self {
Self::Fact => areev_core::types::GrainType::Fact,
Self::Event => areev_core::types::GrainType::Event,
Self::State => areev_core::types::GrainType::State,
Self::Workflow => areev_core::types::GrainType::Workflow,
Self::Tool => areev_core::types::GrainType::Tool,
Self::Observation => areev_core::types::GrainType::Observation,
Self::Goal => areev_core::types::GrainType::Goal,
Self::Reasoning => areev_core::types::GrainType::Reasoning,
Self::Consensus => areev_core::types::GrainType::Consensus,
Self::Consent => areev_core::types::GrainType::Consent,
Self::Skill => areev_core::types::GrainType::Skill,
Self::Recommendation => areev_core::types::GrainType::Recommendation,
}
}
pub fn to_plural(&self) -> GrainTypePlural {
match self {
Self::Fact => GrainTypePlural::Facts,
Self::Event => GrainTypePlural::Events,
Self::State => GrainTypePlural::States,
Self::Workflow => GrainTypePlural::Workflows,
Self::Tool => GrainTypePlural::Tools,
Self::Observation => GrainTypePlural::Observations,
Self::Goal => GrainTypePlural::Goals,
Self::Reasoning => GrainTypePlural::Reasonings,
Self::Consensus => GrainTypePlural::Consensuses,
Self::Consent => GrainTypePlural::Consents,
Self::Skill => GrainTypePlural::Skills,
Self::Recommendation => GrainTypePlural::Recommendations,
}
}
}
impl std::fmt::Display for GrainTypeSingular {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AboutClause {
pub text: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RecentClause {
pub count: u64,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SinceClause {
pub expression: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UntilClause {
pub expression: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LikeClause {
pub text: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BetweenClause {
pub start: String,
pub end: String,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContradictionsClause {
pub inner: Option<Box<CalStatement>>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum FieldDiff {
Added {
field: String,
value: serde_json::Value,
},
Removed {
field: String,
value: serde_json::Value,
},
Changed {
field: String,
old: serde_json::Value,
new: serde_json::Value,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LetBinding {
pub name: String,
pub extractor: Extractor,
pub source: Box<CalStatement>,
#[serde(skip)]
pub span: Option<Span>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Extractor {
Subjects,
Objects,
Hashes,
}
impl std::fmt::Display for Extractor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Subjects => write!(f, "SUBJECTS"),
Self::Objects => write!(f, "OBJECTS"),
Self::Hashes => write!(f, "HASHES"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grain_type_plural_parse() {
assert_eq!(
GrainTypePlural::parse("facts"),
Some(GrainTypePlural::Facts)
);
assert_eq!(
GrainTypePlural::parse("FACTS"),
Some(GrainTypePlural::Facts)
);
assert_eq!(
GrainTypePlural::parse("Events"),
Some(GrainTypePlural::Events)
);
assert_eq!(GrainTypePlural::parse("*"), Some(GrainTypePlural::All));
assert_eq!(GrainTypePlural::parse("grains"), Some(GrainTypePlural::All));
assert_eq!(GrainTypePlural::parse("unknown"), None);
}
#[test]
fn test_grain_type_singular_parse() {
assert_eq!(
GrainTypeSingular::parse("fact"),
Some(GrainTypeSingular::Fact)
);
assert_eq!(
GrainTypeSingular::parse("TOOL"),
Some(GrainTypeSingular::Tool)
);
assert_eq!(GrainTypeSingular::parse("facts"), None); }
#[test]
fn test_grain_type_plural_to_engine_type() {
let plural = GrainTypePlural::Facts;
assert_eq!(plural.to_grain_type(), Some(areev_core::types::GrainType::Fact));
assert_eq!(GrainTypePlural::All.to_grain_type(), None);
}
#[test]
fn test_grain_type_singular_to_plural() {
assert_eq!(GrainTypeSingular::Fact.to_plural(), GrainTypePlural::Facts);
assert_eq!(
GrainTypeSingular::Consent.to_plural(),
GrainTypePlural::Consents
);
}
#[test]
fn test_comparator_display() {
assert_eq!(format!("{}", Comparator::Eq), "=");
assert_eq!(format!("{}", Comparator::NotEq), "!=");
assert_eq!(format!("{}", Comparator::Gte), ">=");
assert_eq!(format!("{}", Comparator::Lt), "<");
}
#[test]
fn test_value_display() {
assert_eq!(
format!(
"{}",
Value::String {
value: "hello".into()
}
),
"\"hello\""
);
assert_eq!(format!("{}", Value::Number { value: 42.0 }), "42");
assert_eq!(format!("{}", Value::Boolean { value: true }), "true");
assert_eq!(format!("{}", Value::Parameter { name: "x".into() }), "$x");
assert_eq!(
format!(
"{}",
Value::Hash {
value: "abc123".into()
}
),
"#abc123"
);
let arr = Value::Array {
values: vec![
Value::String { value: "a".into() },
Value::Number { value: 1.0 },
],
};
assert_eq!(format!("{}", arr), "[\"a\", 1]");
}
#[test]
fn test_cal_version_default() {
assert_eq!(CalVersion::default(), CalVersion(1));
}
#[test]
fn test_extractor_display() {
assert_eq!(format!("{}", Extractor::Subjects), "SUBJECTS");
assert_eq!(format!("{}", Extractor::Objects), "OBJECTS");
assert_eq!(format!("{}", Extractor::Hashes), "HASHES");
}
#[test]
fn test_set_op_serializes() {
let op = SetOp::Intersect;
let json = serde_json::to_string(&op).unwrap();
assert_eq!(json, "\"intersect\"");
}
#[test]
fn test_recall_stmt_construction() {
let stmt = RecallStmt {
grain_type: GrainTypePlural::Facts,
about: Some(AboutClause {
text: "john preferences".into(),
span: None,
}),
where_clause: Some(WhereClause {
condition: Condition::Comparison {
field: "subject".into(),
comparator: Comparator::Eq,
value: Value::String {
value: "john".into(),
},
span: None,
},
span: None,
}),
recent: None,
since: None,
until: None,
like: None,
between: None,
contradictions: None,
limit: Some(10),
as_format: None,
span: None,
};
assert_eq!(stmt.grain_type, GrainTypePlural::Facts);
assert!(stmt.about.is_some());
assert!(stmt.where_clause.is_some());
assert_eq!(stmt.limit, Some(10));
}
#[test]
fn test_cal_query_construction() {
let query = CalQuery {
version: CalVersion(1),
statement: CalStatement::Recall(RecallStmt {
grain_type: GrainTypePlural::Events,
about: None,
where_clause: None,
recent: Some(RecentClause {
count: 5,
span: None,
}),
since: None,
until: None,
like: None,
between: None,
contradictions: None,
limit: None,
as_format: None,
span: None,
}),
pipeline: vec![
PipelineStage::OrderBy {
field: "created_at".into(),
descending: true,
span: None,
},
PipelineStage::Limit {
value: 5,
span: None,
},
],
with_options: vec![WithOption::ScoreBreakdown],
format: Some(FormatClause::Single(FormatSpec::Json)),
let_bindings: vec![],
let_values: Default::default(),
user_vars: HashMap::new(),
warnings: vec![],
};
assert_eq!(query.version, CalVersion(1));
assert_eq!(query.pipeline.len(), 2);
assert_eq!(query.with_options.len(), 1);
}
#[test]
fn test_nested_condition() {
let cond = Condition::And {
left: Box::new(Condition::Comparison {
field: "subject".into(),
comparator: Comparator::Eq,
value: Value::String {
value: "john".into(),
},
span: None,
}),
right: Box::new(Condition::Or {
left: Box::new(Condition::Comparison {
field: "confidence".into(),
comparator: Comparator::Gte,
value: Value::Number { value: 0.8 },
span: None,
}),
right: Box::new(Condition::IsNotNull {
field: "tags".into(),
span: None,
}),
span: None,
}),
span: None,
};
match &cond {
Condition::And { .. } => {}
_ => panic!("expected And"),
}
}
}