1use std::fmt;
4
5use hirn_core::types::{EdgeRelation, Layer};
6
7#[derive(Debug, Clone, PartialEq)]
11pub enum Statement {
12 Recall(Box<RecallStmt>),
13 RecallEvents(RecallEventsStmt),
14 Think(Box<ThinkStmt>),
15 Correct(CorrectStmt),
16 Supersede(SupersedeStmt),
17 MergeMemory(MergeMemoryStmt),
18 Retract(RetractStmt),
19 Inspect(InspectStmt),
20 History(HistoryStmt),
21 Trace(TraceStmt),
22 Traverse(TraverseStmt),
23 Explain(ExplainStmt),
24 ExplainCauses(ExplainCausesStmt),
25 WhatIf(WhatIfStmt),
26 Counterfactual(CounterfactualStmt),
27 CreateRealm(CreateRealmStmt),
28 DropRealm(DropRealmStmt),
29 Grant(GrantStmt),
30 Revoke(RevokeStmt),
31 ShowPolicies(ShowPoliciesStmt),
32 ExplainPolicy(ExplainPolicyStmt),
33 ShowCluster,
34 SetTierPolicy(SetTierPolicyStmt),
35}
36
37#[derive(Debug, Clone, PartialEq)]
40pub struct ExplainStmt {
41 pub analyze: bool,
43 pub inner: Box<Statement>,
45}
46
47#[derive(Debug, Clone, PartialEq)]
51pub struct ExplainCausesStmt {
52 pub target: String,
54 pub namespace: Option<String>,
56 pub depth: Option<usize>,
58}
59
60#[derive(Debug, Clone, PartialEq)]
64pub struct WhatIfStmt {
65 pub intervention: String,
67 pub outcome: String,
69 pub namespace: Option<String>,
71}
72
73#[derive(Debug, Clone, PartialEq)]
77pub struct CounterfactualStmt {
78 pub antecedent: String,
80 pub consequent: String,
82 pub namespace: Option<String>,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum RecallSnapshotAst {
90 Unqualified(String),
91 Observed(String),
92 Recorded(String),
93 Revision(String),
94}
95
96impl fmt::Display for RecallSnapshotAst {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::Unqualified(value) => write!(f, "\"{}\"", EscapeStr(value)),
100 Self::Observed(value) => write!(f, "OBSERVED \"{}\"", EscapeStr(value)),
101 Self::Recorded(value) => write!(f, "RECORDED \"{}\"", EscapeStr(value)),
102 Self::Revision(value) => write!(f, "REVISION \"{}\"", EscapeStr(value)),
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq)]
108pub struct RecallStmt {
109 pub layers: Vec<Layer>,
110 pub about: String,
111 pub involving: Option<Vec<String>>,
112 pub temporal: Option<TemporalClause>,
113 pub as_of: Option<RecallSnapshotAst>,
114 pub expand: Option<ExpandClause>,
115 pub follow_causes: Option<usize>,
116 pub where_clauses: Vec<WhereCondition>,
117 pub subquery_filters: Vec<SubqueryFilter>,
118 pub modality: Option<Vec<String>>,
119 pub resource_roles: Option<Vec<String>>,
120 pub hydration_modes: Option<Vec<String>>,
121 pub artifact_kinds: Option<Vec<String>>,
122 pub depth_mode: Option<DepthModeAst>,
124 pub with_prospective: Option<bool>,
126 pub with_mcfa: Option<bool>,
128 pub with_conflicts: bool,
130 pub provenance_depth: Option<usize>,
132 pub topic: Option<String>,
134 pub group_by: Option<GroupByClause>,
135 pub projection: Option<Vec<String>>,
136 pub output_format: Option<OutputFormat>,
137 pub result_format: Option<OutputFormat>,
138 pub budget: Option<usize>,
139 pub namespace: Option<String>,
140 pub from_realms: Option<Vec<String>>,
142 pub consistency: Option<ConsistencyLevel>,
143 pub limit: Option<usize>,
144 pub hybrid: bool,
146}
147
148#[derive(Debug, Clone, PartialEq)]
151pub struct RecallEventsStmt {
152 pub entity_filter: Option<String>,
154 pub where_clauses: Vec<WhereCondition>,
155 pub temporal: Option<TemporalClause>,
156 pub namespace: Option<String>,
157 pub limit: Option<usize>,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
164pub enum RetrievalMode {
165 #[default]
167 Local,
168 Global,
170 Hybrid,
172 Raptor,
175 Adaptive,
179 Iterative,
181}
182
183#[derive(Debug, Clone, PartialEq)]
184pub struct ThinkStmt {
185 pub about: String,
186 pub involving: Option<Vec<String>>,
187 pub temporal: Option<TemporalClause>,
188 pub expand: Option<ExpandClause>,
189 pub follow_causes: Option<usize>,
190 pub where_clauses: Vec<WhereCondition>,
191 pub output_format: Option<OutputFormat>,
192 pub budget: Option<usize>,
193 pub namespace: Option<String>,
194 pub consistency: Option<ConsistencyLevel>,
195 pub limit: Option<usize>,
196 pub hybrid: bool,
198 pub mode: RetrievalMode,
199 pub depth_mode: Option<DepthModeAst>,
201 pub with_prospective: Option<bool>,
203 pub with_mcfa: Option<bool>,
205 pub provenance_depth: Option<usize>,
207 pub max_hops: Option<usize>,
209 pub community_depth: Option<usize>,
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
216pub enum ModalContent {
217 Image {
218 data: String,
219 description: String,
220 },
221 Code {
222 source: String,
223 language: String,
224 },
225 Audio {
226 data: String,
227 transcript: String,
228 },
229 Video {
230 data: String,
231 transcript: String,
232 description: String,
233 },
234 Document {
235 data: String,
236 title: String,
237 },
238 External {
239 uri: String,
240 title: String,
241 snippet: Option<String>,
242 mime_type: Option<String>,
243 checksum: Option<String>,
244 fetch_policy: Option<String>,
245 stale_at: Option<String>,
246 },
247 ToolOutput {
248 output: String,
249 tool: String,
250 mime_type: Option<String>,
251 schema: Option<String>,
252 call_id: Option<String>,
253 checksum: Option<String>,
254 },
255 Structured {
256 data: String,
257 schema: String,
258 },
259}
260
261#[derive(Debug, Clone, PartialEq)]
263pub struct SetAssignment {
264 pub field: String,
265 pub value: SetValue,
266}
267
268#[derive(Debug, Clone, PartialEq)]
270pub enum SetValue {
271 Float(f64),
272 Int(i64),
273 String(String),
274 Max(String, f64),
275 Min(String, f64),
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
279pub enum ForgetMode {
280 #[default]
281 Archive,
282 Purge,
283 Hard,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq)]
290pub enum SemanticTargetRef {
291 Memory(String),
292 Logical(String),
293 Revision(String),
294}
295
296impl SemanticTargetRef {
297 #[must_use]
298 pub fn raw_value(&self) -> &str {
299 match self {
300 Self::Memory(value) | Self::Logical(value) | Self::Revision(value) => value,
301 }
302 }
303}
304
305#[derive(Debug, Clone, PartialEq)]
306pub struct CorrectStmt {
307 pub target: SemanticTargetRef,
308 pub updates: Vec<SetAssignment>,
309 pub reason: Option<String>,
310 pub observed_at: Option<String>,
311 pub caused_by: Option<String>,
312 pub namespace: Option<String>,
313}
314
315#[derive(Debug, Clone, PartialEq)]
316pub struct SupersedeStmt {
317 pub target: SemanticTargetRef,
318 pub updates: Vec<SetAssignment>,
319 pub reason: Option<String>,
320 pub observed_at: Option<String>,
321 pub caused_by: Option<String>,
322 pub namespace: Option<String>,
323}
324
325#[derive(Debug, Clone, PartialEq)]
326pub struct MergeMemoryStmt {
327 pub sources: Vec<SemanticTargetRef>,
328 pub target: SemanticTargetRef,
329 pub updates: Vec<SetAssignment>,
330 pub reason: Option<String>,
331 pub observed_at: Option<String>,
332 pub caused_by: Option<String>,
333 pub namespace: Option<String>,
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
337pub struct RetractStmt {
338 pub target: SemanticTargetRef,
339 pub reason: Option<String>,
340 pub observed_at: Option<String>,
341 pub caused_by: Option<String>,
342 pub namespace: Option<String>,
343}
344
345#[derive(Debug, Clone, PartialEq, Eq)]
348pub struct InspectStmt {
349 pub target: SemanticTargetRef,
350}
351
352#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct HistoryStmt {
356 pub target: SemanticTargetRef,
357 pub namespace: Option<String>,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
363pub struct TraceStmt {
364 pub target: SemanticTargetRef,
365}
366
367#[derive(Debug, Clone, PartialEq)]
370pub enum TemporalClause {
371 After(String),
372 Before(String),
373 Between { start: String, end: String },
374}
375
376#[derive(Debug, Clone, PartialEq)]
377pub struct ExpandClause {
378 pub depth: usize,
379 pub min_weight: Option<f32>,
380 pub activation: Option<ActivationModeAst>,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum ActivationModeAst {
385 None,
386 Static,
387 Spreading,
388 Ppr,
390}
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
394pub enum DepthModeAst {
395 Auto,
397 Full,
399 Summary,
401}
402
403impl fmt::Display for DepthModeAst {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match self {
406 Self::Auto => write!(f, "AUTO"),
407 Self::Full => write!(f, "FULL"),
408 Self::Summary => write!(f, "SUMMARY"),
409 }
410 }
411}
412
413#[derive(Debug, Clone, PartialEq)]
414pub struct WhereCondition {
415 pub field: String,
416 pub op: ComparisonOp,
417 pub value: ConditionValue,
418}
419
420#[derive(Debug, Clone, Copy, PartialEq, Eq)]
421pub enum ComparisonOp {
422 Gt,
423 Lt,
424 Gte,
425 Lte,
426 Eq,
427 Neq,
428}
429
430#[derive(Debug, Clone, PartialEq)]
431pub enum ConditionValue {
432 Float(f64),
433 Int(i64),
434 String(String),
435 Param(String),
437}
438
439#[derive(Debug, Clone, Copy, PartialEq, Eq)]
440pub enum OutputFormat {
441 Narrative,
442 Context,
443 Graph,
444 CausalChain,
445 Json,
446 Csv,
447 Structured,
448}
449
450#[derive(Debug, Clone, PartialEq)]
452pub struct GroupByClause {
453 pub field: String,
454 pub function: AggFunction,
455}
456
457#[derive(Debug, Clone, Copy, PartialEq, Eq)]
459pub enum AggFunction {
460 Count,
461 Avg,
462 Sum,
463 Min,
464 Max,
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub enum ConsistencyLevel {
469 Linearizable,
470 Eventual,
471 Session,
472}
473
474#[derive(Debug, Clone, PartialEq)]
476pub struct SubqueryFilter {
477 pub field: String,
479 pub subquery: Subquery,
481}
482
483#[derive(Debug, Clone, PartialEq)]
485pub struct Subquery {
486 pub layers: Vec<Layer>,
487 pub about: String,
488 pub involving: Option<Vec<String>>,
489 pub temporal: Option<TemporalClause>,
490 pub limit: Option<usize>,
491}
492
493impl fmt::Display for Statement {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 match self {
498 Self::Recall(s) => write!(f, "{s}"),
499 Self::RecallEvents(s) => write!(f, "{s}"),
500 Self::Think(s) => write!(f, "{s}"),
501 Self::Correct(s) => write!(f, "{s}"),
502 Self::Supersede(s) => write!(f, "{s}"),
503 Self::MergeMemory(s) => write!(f, "{s}"),
504 Self::Retract(s) => write!(f, "{s}"),
505 Self::Inspect(s) => write!(f, "{s}"),
506 Self::History(s) => write!(f, "{s}"),
507 Self::Trace(s) => write!(f, "{s}"),
508 Self::Traverse(s) => write!(f, "{s}"),
509 Self::Explain(s) => write!(f, "{s}"),
510 Self::ExplainCauses(s) => write!(f, "{s}"),
511 Self::WhatIf(s) => write!(f, "{s}"),
512 Self::Counterfactual(s) => write!(f, "{s}"),
513 Self::CreateRealm(s) => write!(f, "{s}"),
514 Self::DropRealm(s) => write!(f, "{s}"),
515 Self::Grant(s) => write!(f, "{s}"),
516 Self::Revoke(s) => write!(f, "{s}"),
517 Self::ShowPolicies(s) => write!(f, "{s}"),
518 Self::ExplainPolicy(s) => write!(f, "{s}"),
519 Self::ShowCluster => write!(f, "SHOW CLUSTER"),
520 Self::SetTierPolicy(s) => write!(f, "{s}"),
521 }
522 }
523}
524
525impl fmt::Display for ExplainStmt {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 write!(f, "EXPLAIN")?;
528 if self.analyze {
529 write!(f, " ANALYZE")?;
530 }
531 write!(f, " {}", self.inner)
532 }
533}
534
535impl fmt::Display for ExplainCausesStmt {
536 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537 write!(f, "EXPLAIN CAUSES \"{}\"", EscapeStr(&self.target))?;
538 if let Some(ref ns) = self.namespace {
539 write!(f, " NAMESPACE {ns}")?;
540 }
541 if let Some(d) = self.depth {
542 write!(f, " DEPTH {d}")?;
543 }
544 Ok(())
545 }
546}
547
548impl fmt::Display for WhatIfStmt {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 write!(
551 f,
552 "WHAT_IF \"{}\" THEN \"{}\"",
553 EscapeStr(&self.intervention),
554 EscapeStr(&self.outcome)
555 )?;
556 if let Some(ref ns) = self.namespace {
557 write!(f, " NAMESPACE {ns}")?;
558 }
559 Ok(())
560 }
561}
562
563impl fmt::Display for CounterfactualStmt {
564 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
565 write!(
566 f,
567 "COUNTERFACTUAL \"{}\" THEN \"{}\"",
568 EscapeStr(&self.antecedent),
569 EscapeStr(&self.consequent)
570 )?;
571 if let Some(ref ns) = self.namespace {
572 write!(f, " NAMESPACE {ns}")?;
573 }
574 Ok(())
575 }
576}
577
578fn write_layer_filter(f: &mut fmt::Formatter<'_>, layers: &[Layer]) -> fmt::Result {
579 for (i, l) in layers.iter().enumerate() {
580 if i > 0 {
581 write!(f, ", ")?;
582 }
583 write!(f, "{}", display_layer(*l))?;
584 }
585 Ok(())
586}
587
588fn display_layer(l: Layer) -> &'static str {
589 match l {
590 Layer::Episodic => "episodic",
591 Layer::Semantic => "semantic",
592 Layer::Working => "working",
593 Layer::Procedural => "procedural",
594 }
595}
596
597#[allow(clippy::too_many_arguments)]
598fn write_shared_clauses(
599 f: &mut fmt::Formatter<'_>,
600 about: &str,
601 involving: Option<&Vec<String>>,
602 temporal: Option<&TemporalClause>,
603 expand: Option<&ExpandClause>,
604 follow_causes: Option<usize>,
605 where_clauses: &[WhereCondition],
606 output_format: Option<OutputFormat>,
607 budget: Option<usize>,
608 namespace: Option<&String>,
609 consistency: Option<ConsistencyLevel>,
610 limit: Option<usize>,
611) -> fmt::Result {
612 write!(f, " ABOUT \"{}\"", EscapeStr(about))?;
613 if let Some(inv) = involving {
614 write!(f, " INVOLVING ")?;
615 write_string_list(f, inv)?;
616 }
617 if let Some(tc) = temporal {
618 write!(f, " {tc}")?;
619 }
620 if let Some(ex) = expand {
621 write!(f, " {ex}")?;
622 }
623 if let Some(d) = follow_causes {
624 write!(f, " FOLLOW CAUSES DEPTH {d}")?;
625 }
626 for wc in where_clauses {
627 write!(f, " {wc}")?;
628 }
629 if let Some(of) = output_format {
630 write!(f, " AS {of}")?;
631 }
632 if let Some(b) = budget {
633 write!(f, " BUDGET {b}")?;
634 }
635 if let Some(ns) = namespace {
636 write!(f, " NAMESPACE {ns}")?;
637 }
638 if let Some(c) = consistency {
639 write!(f, " CONSISTENCY {c}")?;
640 }
641 if let Some(l) = limit {
642 write!(f, " LIMIT {l}")?;
643 }
644 Ok(())
645}
646
647fn write_string_list(f: &mut fmt::Formatter<'_>, items: &[String]) -> fmt::Result {
648 for (i, item) in items.iter().enumerate() {
649 if i > 0 {
650 write!(f, ", ")?;
651 }
652 write!(f, "\"{}\"", EscapeStr(item))?;
653 }
654 Ok(())
655}
656
657fn write_semantic_target_list(
658 f: &mut fmt::Formatter<'_>,
659 items: &[SemanticTargetRef],
660) -> fmt::Result {
661 for (i, item) in items.iter().enumerate() {
662 if i > 0 {
663 write!(f, ", ")?;
664 }
665 write!(f, "{item}")?;
666 }
667 Ok(())
668}
669
670struct EscapeStr<'a>(&'a str);
673
674impl fmt::Display for EscapeStr<'_> {
675 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
676 let s = self.0;
677 let mut start = 0;
678 for (i, ch) in s.char_indices() {
679 let esc = match ch {
680 '\\' => "\\\\",
681 '"' => "\\\"",
682 _ => continue,
683 };
684 f.write_str(&s[start..i])?;
685 f.write_str(esc)?;
686 start = i + ch.len_utf8();
687 }
688 f.write_str(&s[start..])
689 }
690}
691
692impl fmt::Display for SemanticTargetRef {
693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694 match self {
695 Self::Memory(value) => write!(f, "\"{}\"", EscapeStr(value)),
696 Self::Logical(value) => write!(f, "LOGICAL \"{}\"", EscapeStr(value)),
697 Self::Revision(value) => write!(f, "REVISION \"{}\"", EscapeStr(value)),
698 }
699 }
700}
701
702impl fmt::Display for RecallStmt {
703 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704 write!(f, "RECALL ")?;
705 write_layer_filter(f, &self.layers)?;
706 write_shared_clauses(
707 f,
708 &self.about,
709 self.involving.as_ref(),
710 self.temporal.as_ref(),
711 self.expand.as_ref(),
712 self.follow_causes,
713 &self.where_clauses,
714 self.output_format,
715 self.budget,
716 self.namespace.as_ref(),
717 self.consistency,
718 self.limit,
719 )?;
720 if let Some(ref snapshot) = self.as_of {
721 write!(f, " AS OF {snapshot}")?;
722 }
723 for sf in &self.subquery_filters {
724 write!(f, " WHERE {} IN ({})", sf.field, sf.subquery)?;
725 }
726 if let Some(ref modalities) = self.modality {
727 write!(f, " MODALITY {}", modalities.join(", "))?;
728 }
729 if let Some(ref resource_roles) = self.resource_roles {
730 write!(f, " RESOURCE_ROLE {}", resource_roles.join(", "))?;
731 }
732 if let Some(ref hydration_modes) = self.hydration_modes {
733 write!(f, " HYDRATION {}", hydration_modes.join(", "))?;
734 }
735 if let Some(ref artifact_kinds) = self.artifact_kinds {
736 write!(f, " ARTIFACT {}", artifact_kinds.join(", "))?;
737 }
738 if let Some(ref gb) = self.group_by {
739 write!(f, " GROUP BY {} {}", gb.field, gb.function)?;
740 }
741 if let Some(ref proj) = self.projection {
742 write!(f, " SELECT {}", proj.join(", "))?;
743 }
744 if let Some(ref rf) = self.result_format {
745 write!(f, " FORMAT {rf}")?;
746 }
747 if let Some(dm) = self.depth_mode {
748 write!(f, " DEPTH {dm}")?;
749 }
750 if let Some(ref topic) = self.topic {
751 write!(f, " TOPIC \"{}\"", EscapeStr(topic))?;
752 }
753 if let Some(wp) = self.with_prospective {
754 write!(f, " WITH PROSPECTIVE {}", if wp { "ON" } else { "OFF" })?;
755 }
756 if let Some(wm) = self.with_mcfa {
757 write!(f, " WITH MCFA_DEFENSE {}", if wm { "ON" } else { "OFF" })?;
758 }
759 if self.with_conflicts {
760 write!(f, " WITH CONFLICTS")?;
761 }
762 if let Some(pd) = self.provenance_depth {
763 write!(f, " WITH PROVENANCE DEPTH {pd}")?;
764 }
765 if let Some(ref realms) = self.from_realms {
766 write!(f, " FROM REALM ")?;
767 for (i, r) in realms.iter().enumerate() {
768 if i > 0 {
769 write!(f, ", ")?;
770 }
771 write!(f, "\"{}\"", EscapeStr(r))?;
772 }
773 }
774 if self.hybrid {
775 write!(f, " HYBRID")?;
776 }
777 Ok(())
778 }
779}
780
781impl fmt::Display for RecallEventsStmt {
782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
783 write!(f, "RECALL EVENTS")?;
784 if let Some(ref entity) = self.entity_filter {
785 write!(f, " FOR \"{}\"", EscapeStr(entity))?;
786 }
787 for wc in &self.where_clauses {
788 write!(f, " WHERE {wc}")?;
789 }
790 if let Some(ref tc) = self.temporal {
791 write!(f, " {tc}")?;
792 }
793 if let Some(ref ns) = self.namespace {
794 write!(f, " NAMESPACE {ns}")?;
795 }
796 if let Some(l) = self.limit {
797 write!(f, " LIMIT {l}")?;
798 }
799 Ok(())
800 }
801}
802
803impl fmt::Display for ThinkStmt {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 write!(f, "THINK")?;
806 if self.mode == RetrievalMode::Global {
807 write!(f, " GLOBAL")?;
808 }
809 write_shared_clauses(
810 f,
811 &self.about,
812 self.involving.as_ref(),
813 self.temporal.as_ref(),
814 self.expand.as_ref(),
815 self.follow_causes,
816 &self.where_clauses,
817 self.output_format,
818 self.budget,
819 self.namespace.as_ref(),
820 self.consistency,
821 self.limit,
822 )?;
823 match self.mode {
824 RetrievalMode::Hybrid => write!(f, " MODE hybrid")?,
825 RetrievalMode::Raptor => write!(f, " MODE raptor")?,
826 RetrievalMode::Adaptive => write!(f, " MODE adaptive")?,
827 RetrievalMode::Iterative => {
828 write!(f, " MODE iterative")?;
829 if let Some(mh) = self.max_hops {
830 write!(f, " MAX_HOPS {mh}")?;
831 }
832 }
833 _ => {}
834 }
835 if let Some(dm) = self.depth_mode {
836 write!(f, " DEPTH {dm}")?;
837 }
838 if let Some(wp) = self.with_prospective {
839 write!(f, " WITH PROSPECTIVE {}", if wp { "ON" } else { "OFF" })?;
840 }
841 if let Some(wm) = self.with_mcfa {
842 write!(f, " WITH MCFA_DEFENSE {}", if wm { "ON" } else { "OFF" })?;
843 }
844 if let Some(pd) = self.provenance_depth {
845 write!(f, " WITH PROVENANCE DEPTH {pd}")?;
846 }
847 if let Some(depth) = self.community_depth {
848 write!(f, " COMMUNITY_DEPTH {depth}")?;
849 }
850 if self.hybrid {
851 write!(f, " HYBRID")?;
852 }
853 Ok(())
854 }
855}
856
857impl fmt::Display for SetAssignment {
858 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
859 write!(f, "{} = {}", self.field, self.value)
860 }
861}
862
863impl fmt::Display for SetValue {
864 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865 match self {
866 Self::Float(v) => write!(f, "{v}"),
867 Self::Int(v) => write!(f, "{v}"),
868 Self::String(v) => write!(f, "\"{v}\""),
869 Self::Max(field, val) => write!(f, "MAX({field}, {val})"),
870 Self::Min(field, val) => write!(f, "MIN({field}, {val})"),
871 }
872 }
873}
874
875impl fmt::Display for CorrectStmt {
876 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
877 write!(f, "CORRECT {} SET ", self.target)?;
878 for (i, update) in self.updates.iter().enumerate() {
879 if i > 0 {
880 write!(f, ", ")?;
881 }
882 write!(f, "{update}")?;
883 }
884 if let Some(ref reason) = self.reason {
885 write!(f, " REASON \"{}\"", EscapeStr(reason))?;
886 }
887 if let Some(ref observed_at) = self.observed_at {
888 write!(f, " OBSERVED AT \"{}\"", EscapeStr(observed_at))?;
889 }
890 if let Some(ref caused_by) = self.caused_by {
891 write!(f, " CAUSED BY \"{}\"", EscapeStr(caused_by))?;
892 }
893 if let Some(ref namespace) = self.namespace {
894 write!(f, " NAMESPACE {namespace}")?;
895 }
896 Ok(())
897 }
898}
899
900impl fmt::Display for SupersedeStmt {
901 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
902 write!(f, "SUPERSEDE {} SET ", self.target)?;
903 for (i, update) in self.updates.iter().enumerate() {
904 if i > 0 {
905 write!(f, ", ")?;
906 }
907 write!(f, "{update}")?;
908 }
909 if let Some(ref reason) = self.reason {
910 write!(f, " REASON \"{}\"", EscapeStr(reason))?;
911 }
912 if let Some(ref observed_at) = self.observed_at {
913 write!(f, " OBSERVED AT \"{}\"", EscapeStr(observed_at))?;
914 }
915 if let Some(ref caused_by) = self.caused_by {
916 write!(f, " CAUSED BY \"{}\"", EscapeStr(caused_by))?;
917 }
918 if let Some(ref namespace) = self.namespace {
919 write!(f, " NAMESPACE {namespace}")?;
920 }
921 Ok(())
922 }
923}
924
925impl fmt::Display for MergeMemoryStmt {
926 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
927 write!(f, "MERGE MEMORY ")?;
928 write_semantic_target_list(f, &self.sources)?;
929 write!(f, " INTO {}", self.target)?;
930 if !self.updates.is_empty() {
931 write!(f, " SET ")?;
932 for (i, update) in self.updates.iter().enumerate() {
933 if i > 0 {
934 write!(f, ", ")?;
935 }
936 write!(f, "{update}")?;
937 }
938 }
939 if let Some(ref reason) = self.reason {
940 write!(f, " REASON \"{}\"", EscapeStr(reason))?;
941 }
942 if let Some(ref observed_at) = self.observed_at {
943 write!(f, " OBSERVED AT \"{}\"", EscapeStr(observed_at))?;
944 }
945 if let Some(ref caused_by) = self.caused_by {
946 write!(f, " CAUSED BY \"{}\"", EscapeStr(caused_by))?;
947 }
948 if let Some(ref namespace) = self.namespace {
949 write!(f, " NAMESPACE {namespace}")?;
950 }
951 Ok(())
952 }
953}
954
955impl fmt::Display for RetractStmt {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 write!(f, "RETRACT {}", self.target)?;
958 if let Some(ref reason) = self.reason {
959 write!(f, " REASON \"{}\"", EscapeStr(reason))?;
960 }
961 if let Some(ref observed_at) = self.observed_at {
962 write!(f, " OBSERVED AT \"{}\"", EscapeStr(observed_at))?;
963 }
964 if let Some(ref caused_by) = self.caused_by {
965 write!(f, " CAUSED BY \"{}\"", EscapeStr(caused_by))?;
966 }
967 if let Some(ref namespace) = self.namespace {
968 write!(f, " NAMESPACE {namespace}")?;
969 }
970 Ok(())
971 }
972}
973
974impl fmt::Display for InspectStmt {
975 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
976 write!(f, "INSPECT {}", self.target)
977 }
978}
979
980impl fmt::Display for HistoryStmt {
981 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
982 write!(f, "HISTORY {}", self.target)?;
983 if let Some(ref namespace) = self.namespace {
984 write!(f, " NAMESPACE {namespace}")?;
985 }
986 Ok(())
987 }
988}
989
990impl fmt::Display for TraceStmt {
991 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992 write!(f, "TRACE {}", self.target)
993 }
994}
995
996impl fmt::Display for TemporalClause {
997 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
998 match self {
999 Self::After(ts) => write!(f, "AFTER \"{ts}\""),
1000 Self::Before(ts) => write!(f, "BEFORE \"{ts}\""),
1001 Self::Between { start, end } => write!(f, "BETWEEN \"{start}\" AND \"{end}\""),
1002 }
1003 }
1004}
1005
1006impl fmt::Display for ExpandClause {
1007 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1008 write!(f, "EXPAND GRAPH DEPTH {}", self.depth)?;
1009 if let Some(mw) = self.min_weight {
1010 write!(f, " MIN_WEIGHT {mw}")?;
1011 }
1012 if let Some(am) = self.activation {
1013 write!(f, " ACTIVATION {am}")?;
1014 }
1015 Ok(())
1016 }
1017}
1018
1019impl fmt::Display for ActivationModeAst {
1020 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1021 match self {
1022 Self::None => write!(f, "none"),
1023 Self::Static => write!(f, "static"),
1024 Self::Spreading => write!(f, "spreading"),
1025 Self::Ppr => write!(f, "ppr"),
1026 }
1027 }
1028}
1029
1030impl fmt::Display for ComparisonOp {
1031 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1032 match self {
1033 Self::Gt => write!(f, ">"),
1034 Self::Lt => write!(f, "<"),
1035 Self::Gte => write!(f, ">="),
1036 Self::Lte => write!(f, "<="),
1037 Self::Eq => write!(f, "="),
1038 Self::Neq => write!(f, "!="),
1039 }
1040 }
1041}
1042
1043impl fmt::Display for ConditionValue {
1044 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1045 match self {
1046 Self::Float(value) => write!(f, "{value}"),
1047 Self::Int(value) => write!(f, "{value}"),
1048 Self::String(value) => write!(f, "\"{}\"", EscapeStr(value)),
1049 Self::Param(value) => write!(f, "{value}"),
1050 }
1051 }
1052}
1053
1054impl fmt::Display for OutputFormat {
1055 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1056 match self {
1057 Self::Narrative => write!(f, "narrative"),
1058 Self::Context => write!(f, "context"),
1059 Self::Graph => write!(f, "graph"),
1060 Self::CausalChain => write!(f, "causal_chain"),
1061 Self::Json => write!(f, "json"),
1062 Self::Csv => write!(f, "csv"),
1063 Self::Structured => write!(f, "structured"),
1064 }
1065 }
1066}
1067
1068impl fmt::Display for WhereCondition {
1069 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1070 write!(f, "WHERE {} {} {}", self.field, self.op, self.value)
1071 }
1072}
1073
1074impl fmt::Display for ConsistencyLevel {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 match self {
1077 Self::Linearizable => write!(f, "linearizable"),
1078 Self::Eventual => write!(f, "eventual"),
1079 Self::Session => write!(f, "session"),
1080 }
1081 }
1082}
1083
1084impl fmt::Display for AggFunction {
1085 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1086 match self {
1087 Self::Count => write!(f, "COUNT"),
1088 Self::Avg => write!(f, "AVG"),
1089 Self::Sum => write!(f, "SUM"),
1090 Self::Min => write!(f, "MIN"),
1091 Self::Max => write!(f, "MAX"),
1092 }
1093 }
1094}
1095
1096impl fmt::Display for Subquery {
1097 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1098 write!(f, "RECALL ")?;
1099 write_layer_filter(f, &self.layers)?;
1100 write!(f, " ABOUT \"{}\"", self.about)?;
1101 if let Some(ref inv) = self.involving {
1102 write!(f, " INVOLVING ")?;
1103 write_string_list(f, inv)?;
1104 }
1105 if let Some(ref tc) = self.temporal {
1106 write!(f, " {tc}")?;
1107 }
1108 if let Some(n) = self.limit {
1109 write!(f, " LIMIT {n}")?;
1110 }
1111 Ok(())
1112 }
1113}
1114
1115#[derive(Debug, Clone, PartialEq)]
1118pub struct TraverseStmt {
1119 pub from: String,
1120 pub via: Option<Vec<String>>,
1121 pub depth: usize,
1122 pub where_clauses: Vec<WhereCondition>,
1123 pub limit: Option<usize>,
1124 pub namespace: Option<String>,
1126}
1127impl fmt::Display for TraverseStmt {
1128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1129 write!(f, "TRAVERSE FROM \"{}\"", EscapeStr(&self.from))?;
1130 if let Some(ref via) = self.via {
1131 write!(f, " VIA {}", via.join(", "))?;
1132 }
1133 write!(f, " DEPTH {}", self.depth)?;
1134 if let Some(ref ns) = self.namespace {
1135 write!(f, " NAMESPACE \"{}\"", EscapeStr(ns))?;
1136 }
1137 for wc in &self.where_clauses {
1138 write!(f, " {wc}")?;
1139 }
1140 if let Some(n) = self.limit {
1141 write!(f, " LIMIT {n}")?;
1142 }
1143 Ok(())
1144 }
1145}
1146pub fn parse_edge_relation(s: &str) -> Option<EdgeRelation> {
1150 const TABLE: &[(&str, EdgeRelation)] = &[
1151 ("related_to", EdgeRelation::RelatedTo),
1152 ("relatedto", EdgeRelation::RelatedTo),
1153 ("causes", EdgeRelation::Causes),
1154 ("caused_by", EdgeRelation::CausedBy),
1155 ("causedby", EdgeRelation::CausedBy),
1156 ("derived_from", EdgeRelation::DerivedFrom),
1157 ("derivedfrom", EdgeRelation::DerivedFrom),
1158 ("contradicts", EdgeRelation::Contradicts),
1159 ("supports", EdgeRelation::Supports),
1160 ("temporal_next", EdgeRelation::TemporalNext),
1161 ("temporalnext", EdgeRelation::TemporalNext),
1162 ("part_of", EdgeRelation::PartOf),
1163 ("partof", EdgeRelation::PartOf),
1164 ("instance_of", EdgeRelation::InstanceOf),
1165 ("instanceof", EdgeRelation::InstanceOf),
1166 ("similar_to", EdgeRelation::SimilarTo),
1167 ("similarto", EdgeRelation::SimilarTo),
1168 ("inhibits", EdgeRelation::Inhibits),
1169 ("participates_in", EdgeRelation::ParticipatesIn),
1170 ("participatesin", EdgeRelation::ParticipatesIn),
1171 ];
1172 TABLE
1173 .iter()
1174 .find(|(k, _)| s.eq_ignore_ascii_case(k))
1175 .map(|(_, v)| *v)
1176}
1177
1178#[derive(Debug, Clone, PartialEq, Eq)]
1181pub struct CreateRealmStmt {
1182 pub name: String,
1183 pub description: Option<String>,
1184}
1185
1186impl fmt::Display for CreateRealmStmt {
1187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1188 write!(f, "CREATE REALM \"{}\"", EscapeStr(&self.name))?;
1189 if let Some(ref desc) = self.description {
1190 write!(f, " DESCRIPTION \"{}\"", EscapeStr(desc))?;
1191 }
1192 Ok(())
1193 }
1194}
1195
1196#[derive(Debug, Clone, PartialEq, Eq)]
1197pub struct DropRealmStmt {
1198 pub name: String,
1199 pub confirm: bool,
1200}
1201
1202impl fmt::Display for DropRealmStmt {
1203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1204 write!(f, "DROP REALM \"{}\"", EscapeStr(&self.name))?;
1205 if self.confirm {
1206 write!(f, " CONFIRM")?;
1207 }
1208 Ok(())
1209 }
1210}
1211
1212#[derive(Debug, Clone, PartialEq, Eq)]
1216pub enum GrantTarget {
1217 Namespace(String),
1218 Realm(String),
1219}
1220
1221#[derive(Debug, Clone, PartialEq, Eq)]
1223pub enum PrincipalRef {
1224 Agent(String),
1225 Team(String),
1226}
1227
1228impl PrincipalRef {
1229 pub fn id(&self) -> &str {
1231 match self {
1232 Self::Agent(a) => a,
1233 Self::Team(t) => t,
1234 }
1235 }
1236}
1237
1238#[derive(Debug, Clone, PartialEq, Eq)]
1239pub struct GrantStmt {
1240 pub actions: Vec<String>,
1241 pub target: GrantTarget,
1242 pub principal: PrincipalRef,
1243}
1244
1245impl fmt::Display for GrantStmt {
1246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1247 write!(f, "GRANT {}", self.actions.join(", "))?;
1248 match &self.target {
1249 GrantTarget::Namespace(ns) => write!(f, " ON NAMESPACE \"{}\"", EscapeStr(ns))?,
1250 GrantTarget::Realm(r) => write!(f, " ON REALM \"{}\"", EscapeStr(r))?,
1251 }
1252 match &self.principal {
1253 PrincipalRef::Agent(a) => write!(f, " TO AGENT \"{}\"", EscapeStr(a))?,
1254 PrincipalRef::Team(t) => write!(f, " TO TEAM \"{}\"", EscapeStr(t))?,
1255 }
1256 Ok(())
1257 }
1258}
1259
1260#[derive(Debug, Clone, PartialEq, Eq)]
1261pub struct RevokeStmt {
1262 pub actions: Vec<String>,
1263 pub target: GrantTarget,
1264 pub principal: PrincipalRef,
1265}
1266
1267impl fmt::Display for RevokeStmt {
1268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1269 write!(f, "REVOKE {}", self.actions.join(", "))?;
1270 match &self.target {
1271 GrantTarget::Namespace(ns) => write!(f, " ON NAMESPACE \"{}\"", EscapeStr(ns))?,
1272 GrantTarget::Realm(r) => write!(f, " ON REALM \"{}\"", EscapeStr(r))?,
1273 }
1274 match &self.principal {
1275 PrincipalRef::Agent(a) => write!(f, " FROM AGENT \"{}\"", EscapeStr(a))?,
1276 PrincipalRef::Team(t) => write!(f, " FROM TEAM \"{}\"", EscapeStr(t))?,
1277 }
1278 Ok(())
1279 }
1280}
1281
1282#[derive(Debug, Clone, PartialEq, Eq)]
1285pub struct ShowPoliciesStmt {
1286 pub principal: Option<PrincipalRef>,
1287}
1288
1289impl fmt::Display for ShowPoliciesStmt {
1290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1291 write!(f, "SHOW POLICIES")?;
1292 if let Some(ref p) = self.principal {
1293 match p {
1294 PrincipalRef::Agent(a) => write!(f, " FOR AGENT \"{}\"", EscapeStr(a))?,
1295 PrincipalRef::Team(t) => write!(f, " FOR TEAM \"{}\"", EscapeStr(t))?,
1296 }
1297 }
1298 Ok(())
1299 }
1300}
1301
1302#[derive(Debug, Clone, PartialEq, Eq)]
1303pub struct ExplainPolicyStmt {
1304 pub principal: PrincipalRef,
1305 pub resource_type: String,
1306 pub resource_name: String,
1307 pub action: String,
1308}
1309
1310impl fmt::Display for ExplainPolicyStmt {
1311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1312 write!(f, "EXPLAIN POLICY FOR ")?;
1313 match &self.principal {
1314 PrincipalRef::Agent(a) => write!(f, "AGENT \"{}\"", EscapeStr(a))?,
1315 PrincipalRef::Team(t) => write!(f, "TEAM \"{}\"", EscapeStr(t))?,
1316 }
1317 write!(
1318 f,
1319 " ON {} \"{}\" ACTION {}",
1320 self.resource_type.to_uppercase(),
1321 EscapeStr(&self.resource_name),
1322 self.action
1323 )
1324 }
1325}
1326
1327#[derive(Debug, Clone, PartialEq)]
1331pub enum TierPolicyValue {
1332 Str(String),
1334 Float(f64),
1336 Int(i64),
1338}
1339
1340impl fmt::Display for TierPolicyValue {
1341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1342 match self {
1343 Self::Str(s) => write!(f, "'{s}'"),
1344 Self::Float(v) => write!(f, "{v}"),
1345 Self::Int(v) => write!(f, "{v}"),
1346 }
1347 }
1348}
1349
1350#[derive(Debug, Clone, PartialEq)]
1352pub struct SetTierPolicyStmt {
1353 pub field: String,
1355 pub value: TierPolicyValue,
1357}
1358
1359impl fmt::Display for SetTierPolicyStmt {
1360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1361 write!(f, "SET TIER_POLICY {} = ", self.field)?;
1362 match &self.value {
1363 TierPolicyValue::Str(s) => write!(f, "'{}'", EscapeStr(s)),
1364 TierPolicyValue::Float(v) => write!(f, "{v}"),
1365 TierPolicyValue::Int(v) => write!(f, "{v}"),
1366 }
1367 }
1368}
1369
1370pub fn is_param(s: &str) -> bool {
1372 s.starts_with('$')
1373 && s.len() > 1
1374 && s[1..]
1375 .chars()
1376 .all(|c| c.is_ascii_alphanumeric() || c == '_')
1377}
1378
1379pub fn collect_parameters(stmt: &Statement) -> Vec<String> {
1384 let mut params = Vec::new();
1385
1386 fn check(s: &str, params: &mut Vec<String>) {
1387 if is_param(s) && !params.contains(&s.to_string()) {
1388 params.push(s.to_string());
1389 }
1390 }
1391
1392 fn check_wheres(wcs: &[WhereCondition], params: &mut Vec<String>) {
1393 for wc in wcs {
1394 if let ConditionValue::Param(ref p) = wc.value
1395 && !params.contains(p)
1396 {
1397 params.push(p.clone());
1398 }
1399 }
1400 }
1401
1402 match stmt {
1403 Statement::Recall(r) => {
1404 check(&r.about, &mut params);
1405 check_wheres(&r.where_clauses, &mut params);
1406 }
1407 Statement::Think(t) => {
1408 check(&t.about, &mut params);
1409 check_wheres(&t.where_clauses, &mut params);
1410 }
1411 Statement::Correct(c) => {
1412 check(c.target.raw_value(), &mut params);
1413 if let Some(ref reason) = c.reason {
1414 check(reason, &mut params);
1415 }
1416 if let Some(ref observed_at) = c.observed_at {
1417 check(observed_at, &mut params);
1418 }
1419 if let Some(ref caused_by) = c.caused_by {
1420 check(caused_by, &mut params);
1421 }
1422 }
1423 Statement::Supersede(s) => {
1424 check(s.target.raw_value(), &mut params);
1425 if let Some(ref reason) = s.reason {
1426 check(reason, &mut params);
1427 }
1428 if let Some(ref observed_at) = s.observed_at {
1429 check(observed_at, &mut params);
1430 }
1431 if let Some(ref caused_by) = s.caused_by {
1432 check(caused_by, &mut params);
1433 }
1434 }
1435 Statement::MergeMemory(m) => {
1436 for source in &m.sources {
1437 check(source.raw_value(), &mut params);
1438 }
1439 check(m.target.raw_value(), &mut params);
1440 if let Some(ref reason) = m.reason {
1441 check(reason, &mut params);
1442 }
1443 if let Some(ref observed_at) = m.observed_at {
1444 check(observed_at, &mut params);
1445 }
1446 if let Some(ref caused_by) = m.caused_by {
1447 check(caused_by, &mut params);
1448 }
1449 }
1450 Statement::Retract(r) => {
1451 check(r.target.raw_value(), &mut params);
1452 if let Some(ref reason) = r.reason {
1453 check(reason, &mut params);
1454 }
1455 if let Some(ref observed_at) = r.observed_at {
1456 check(observed_at, &mut params);
1457 }
1458 if let Some(ref caused_by) = r.caused_by {
1459 check(caused_by, &mut params);
1460 }
1461 }
1462 Statement::Traverse(t) => {
1463 check(&t.from, &mut params);
1464 check_wheres(&t.where_clauses, &mut params);
1465 }
1466 Statement::Inspect(i) => check(i.target.raw_value(), &mut params),
1467 Statement::History(h) => check(h.target.raw_value(), &mut params),
1468 Statement::Trace(t) => check(t.target.raw_value(), &mut params),
1469 Statement::Explain(e) => return collect_parameters(&e.inner),
1470 Statement::RecallEvents(r) => check_wheres(&r.where_clauses, &mut params),
1471 Statement::CreateRealm(_)
1472 | Statement::DropRealm(_)
1473 | Statement::Grant(_)
1474 | Statement::Revoke(_)
1475 | Statement::ShowPolicies(_)
1476 | Statement::ExplainPolicy(_)
1477 | Statement::ShowCluster
1478 | Statement::SetTierPolicy(_) => {}
1479 Statement::ExplainCauses(e) => check(&e.target, &mut params),
1480 Statement::WhatIf(w) => {
1481 check(&w.intervention, &mut params);
1482 check(&w.outcome, &mut params);
1483 }
1484 Statement::Counterfactual(c) => {
1485 check(&c.antecedent, &mut params);
1486 check(&c.consequent, &mut params);
1487 }
1488 }
1489
1490 params.sort();
1491 params
1492}
1493
1494#[cfg(test)]
1495mod tests {
1496 use super::*;
1497
1498 #[test]
1499 fn display_recall() {
1500 let stmt = RecallStmt {
1501 layers: vec![Layer::Episodic, Layer::Semantic],
1502 about: "test query".into(),
1503 involving: None,
1504 temporal: None,
1505 expand: None,
1506 follow_causes: None,
1507 where_clauses: vec![],
1508 modality: None,
1509 resource_roles: None,
1510 hydration_modes: None,
1511 artifact_kinds: None,
1512 group_by: None,
1513 projection: None,
1514 output_format: None,
1515 result_format: None,
1516 as_of: None,
1517 subquery_filters: vec![],
1518 budget: None,
1519 namespace: None,
1520 consistency: None,
1521 limit: Some(10),
1522 hybrid: false,
1523 depth_mode: None,
1524 with_prospective: None,
1525 with_mcfa: None,
1526 with_conflicts: false,
1527 provenance_depth: None,
1528 topic: None,
1529 from_realms: None,
1530 };
1531 let s = stmt.to_string();
1532 assert!(s.starts_with("RECALL episodic, semantic ABOUT"));
1533 assert!(s.contains("LIMIT 10"));
1534 }
1535
1536 #[test]
1537 fn display_all_variants() {
1538 let stmts: Vec<Statement> = vec![
1540 Statement::Recall(Box::new(RecallStmt {
1541 layers: vec![Layer::Episodic],
1542 about: "x".into(),
1543 involving: None,
1544 temporal: None,
1545 expand: None,
1546 follow_causes: None,
1547 where_clauses: vec![],
1548 modality: None,
1549 resource_roles: None,
1550 hydration_modes: None,
1551 artifact_kinds: None,
1552 group_by: None,
1553 projection: None,
1554 output_format: None,
1555 result_format: None,
1556 as_of: None,
1557 subquery_filters: vec![],
1558 budget: None,
1559 namespace: None,
1560 consistency: None,
1561 limit: None,
1562 hybrid: false,
1563 depth_mode: None,
1564 with_prospective: None,
1565 with_mcfa: None,
1566 with_conflicts: false,
1567 provenance_depth: None,
1568 topic: None,
1569 from_realms: None,
1570 })),
1571 Statement::Think(Box::new(ThinkStmt {
1572 about: "y".into(),
1573 involving: None,
1574 temporal: None,
1575 expand: None,
1576 follow_causes: None,
1577 where_clauses: vec![],
1578 output_format: None,
1579 budget: Some(4096),
1580 namespace: None,
1581 consistency: None,
1582 limit: None,
1583 hybrid: false,
1584 mode: RetrievalMode::Local,
1585 community_depth: None,
1586 depth_mode: None,
1587 with_prospective: None,
1588 with_mcfa: None,
1589 provenance_depth: None,
1590 max_hops: None,
1591 })),
1592 Statement::Correct(CorrectStmt {
1593 target: SemanticTargetRef::Memory("some_id".into()),
1594 updates: vec![SetAssignment {
1595 field: "description".into(),
1596 value: SetValue::String("updated".into()),
1597 }],
1598 reason: Some("fix".into()),
1599 observed_at: None,
1600 caused_by: None,
1601 namespace: None,
1602 }),
1603 Statement::Supersede(SupersedeStmt {
1604 target: SemanticTargetRef::Memory("some_id".into()),
1605 updates: vec![SetAssignment {
1606 field: "description".into(),
1607 value: SetValue::String("replacement".into()),
1608 }],
1609 reason: Some("new authority".into()),
1610 observed_at: None,
1611 caused_by: None,
1612 namespace: None,
1613 }),
1614 Statement::MergeMemory(MergeMemoryStmt {
1615 sources: vec![
1616 SemanticTargetRef::Memory("some_id".into()),
1617 SemanticTargetRef::Logical("other_id".into()),
1618 ],
1619 target: SemanticTargetRef::Revision("target_id".into()),
1620 updates: vec![SetAssignment {
1621 field: "confidence".into(),
1622 value: SetValue::Float(0.92),
1623 }],
1624 reason: Some("deduplicate".into()),
1625 observed_at: None,
1626 caused_by: None,
1627 namespace: None,
1628 }),
1629 Statement::Retract(RetractStmt {
1630 target: SemanticTargetRef::Memory("some_id".into()),
1631 reason: Some("obsolete".into()),
1632 observed_at: None,
1633 caused_by: None,
1634 namespace: None,
1635 }),
1636 Statement::Inspect(InspectStmt {
1637 target: SemanticTargetRef::Logical("id".into()),
1638 }),
1639 Statement::History(HistoryStmt {
1640 target: SemanticTargetRef::Revision("id".into()),
1641 namespace: Some("team_a".into()),
1642 }),
1643 Statement::Trace(TraceStmt {
1644 target: SemanticTargetRef::Memory("id".into()),
1645 }),
1646 ];
1647 for s in &stmts {
1648 let text = s.to_string();
1649 assert!(!text.is_empty(), "Display for {s:?} should not be empty");
1650 }
1651 }
1652
1653 #[test]
1654 fn parse_edge_relation_cases() {
1655 assert_eq!(
1656 parse_edge_relation("related_to"),
1657 Some(EdgeRelation::RelatedTo)
1658 );
1659 assert_eq!(parse_edge_relation("causes"), Some(EdgeRelation::Causes));
1660 assert_eq!(
1661 parse_edge_relation("CONTRADICTS"),
1662 Some(EdgeRelation::Contradicts)
1663 );
1664 assert_eq!(parse_edge_relation("unknown"), None);
1665 }
1666}