akar_function/scalar/
path.rs1use crate::registry::*;
2use akar_common::types::Value;
3
4pub(crate) fn evaluate_path(op: PathOp, args: &[Value]) -> Result<Value, String> {
7 match op {
8 PathOp::Nodes => {
9 let path = &args[0];
10 match path {
11 Value::Struct(fields) => {
12 if let Some((_, nodes_val)) = fields.iter().find(|(k, _)| k == "_nodes") {
14 Ok(nodes_val.clone())
15 } else if let Some((_, first)) = fields.first() {
16 Ok(first.clone())
18 } else {
19 Ok(Value::Null)
20 }
21 }
22 Value::List(_) => Ok(path.clone()),
23 _ => Err(format!("NODES() requires a path/recursive rel, got {:?}", path)),
24 }
25 }
26 PathOp::Rels => {
27 let path = &args[0];
28 match path {
29 Value::Struct(fields) => {
30 if let Some((_, rels_val)) = fields.iter().find(|(k, _)| k == "_rels") {
31 Ok(rels_val.clone())
32 } else if fields.len() >= 2 {
33 Ok(fields[1].1.clone())
34 } else {
35 Ok(Value::Null)
36 }
37 }
38 _ => Err(format!("RELS() requires a path/recursive rel, got {:?}", path)),
39 }
40 }
41 PathOp::Length => {
42 let path = &args[0];
43 match path {
44 Value::List(items) => Ok(Value::Int64(items.len() as i64)),
45 Value::Struct(fields) => {
46 if let Some((_, Value::List(rels))) = fields.iter().find(|(k, _)| k == "_rels") {
48 Ok(Value::Int64(rels.len() as i64))
49 } else {
50 Ok(Value::Int64(0))
51 }
52 }
53 _ => Err(format!("LENGTH() requires a path/recursive rel, got {:?}", path)),
54 }
55 }
56 PathOp::Properties => {
57 let path = &args[0];
58 match path {
59 Value::Struct(fields) => {
60 let mut props: Vec<(Value, Value)> = Vec::new();
61 for (key, val) in fields {
62 if key != "_nodes" && key != "_rels" && key != "_src" && key != "_dst" {
63 props.push((Value::String(key.clone()), val.clone()));
64 }
65 }
66 Ok(Value::Map(props))
67 }
68 Value::List(items) => {
69 let mut all_props: Vec<(Value, Value)> = Vec::new();
70 for (i, item) in items.iter().enumerate() {
71 all_props.push((Value::Int64(i as i64), item.clone()));
72 }
73 Ok(Value::Map(all_props))
74 }
75 _ => Err(format!("PROPERTIES() requires a path, got {:?}", path)),
76 }
77 }
78 PathOp::IsTrail => {
79 let path = &args[0];
80 let result = match path {
81 Value::Struct(fields) => {
82 if let Some((_, Value::List(rels))) = fields.iter().find(|(k, _)| k == "_rels") {
83 !has_duplicates(rels)
84 } else {
85 true
86 }
87 }
88 Value::List(items) => !has_duplicates(items),
89 _ => true,
90 };
91 Ok(Value::Bool(result))
92 }
93 PathOp::IsAcyclic => {
94 let path = &args[0];
95 let result = match path {
96 Value::Struct(fields) => {
97 if let Some((_, Value::List(nodes))) = fields.iter().find(|(k, _)| k == "_nodes") {
98 !has_duplicates(nodes)
99 } else {
100 true
101 }
102 }
103 Value::List(items) => !has_duplicates(items),
104 _ => true,
105 };
106 Ok(Value::Bool(result))
107 }
108 }
109}
110
111fn has_duplicates(items: &[Value]) -> bool {
113 for i in 0..items.len() {
114 for j in (i + 1)..items.len() {
115 if items[i] == items[j] {
116 return true;
117 }
118 }
119 }
120 false
121}
122
123pub(crate) fn evaluate_uuid(_args: &[Value]) -> Result<Value, String> {
125 use std::time::{SystemTime, UNIX_EPOCH};
126 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default();
128 let mut seed = now.as_nanos() as u64;
129 seed ^= seed >> 12;
131 seed ^= seed << 25;
132 seed ^= seed >> 27;
133 let r1 = seed.wrapping_mul(0x2545F4914F6CDD1Du64);
134 seed ^= seed >> 12;
135 seed ^= seed << 25;
136 seed ^= seed >> 27;
137 let r2 = seed.wrapping_mul(0x2545F4914F6CDD1Du64);
138
139 let time_low = (r1 & 0xFFFFFFFF) as u32;
141 let time_mid = ((r1 >> 32) & 0xFFFF) as u16;
142 let time_hi_and_version = (((r1 >> 48) & 0x0FFF) | 0x4000) as u16; let clock_seq = ((r2 & 0x3FFF) | 0x8000) as u16; let node_low = ((r2 >> 14) & 0xFFFFFFFF) as u32;
145 let node_hi = ((r2 >> 46) & 0xFFFF) as u16;
146
147 Ok(Value::String(format!(
148 "{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}",
149 time_low, time_mid, time_hi_and_version, clock_seq, node_hi, node_low
150 )))
151}