1use std::{
4 borrow::Cow,
5 cmp::Ordering,
6 collections::BTreeMap,
7 fmt::Debug,
8 num::NonZeroUsize,
9 ops::Index,
10 sync::{Arc, OnceLock},
11};
12
13use serde::{Deserialize, Serialize};
14
15pub use self::indexed::{EdgeKind, IndexedQuery, InvalidIRQueryError, Output};
16pub use self::types::{NamedTypedValue, Type};
17pub use self::value::{FieldValue, TransparentValue};
18
19mod indexed;
20mod types;
21pub mod value;
22
23pub(crate) const TYPENAME_META_FIELD: &str = "__typename";
24
25static TYPENAME_META_FIELD_ARC: OnceLock<Arc<str>> = OnceLock::new();
26
27pub(crate) fn get_typename_meta_field() -> &'static Arc<str> {
28 TYPENAME_META_FIELD_ARC.get_or_init(|| Arc::from(TYPENAME_META_FIELD))
29}
30
31#[doc(alias("vertex", "node"))]
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
34pub struct Vid(pub(crate) NonZeroUsize);
35
36impl Vid {
37 pub fn new(id: NonZeroUsize) -> Vid {
38 Vid(id)
39 }
40}
41
42#[doc(alias = "edge")]
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
45pub struct Eid(pub(crate) NonZeroUsize);
46
47impl Eid {
48 pub fn new(id: NonZeroUsize) -> Eid {
49 Eid(id)
50 }
51}
52
53#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
63pub struct EdgeParameters {
64 pub(crate) contents: Arc<BTreeMap<Arc<str>, FieldValue>>,
65}
66
67impl EdgeParameters {
68 pub(crate) fn new(contents: Arc<BTreeMap<Arc<str>, FieldValue>>) -> Self {
69 Self { contents }
70 }
71
72 pub fn get(&self, name: &str) -> Option<&FieldValue> {
78 self.contents.get(name)
79 }
80
81 pub fn iter(&self) -> impl Iterator<Item = (&'_ Arc<str>, &'_ FieldValue)> + '_ {
83 self.contents.iter()
84 }
85
86 pub fn is_empty(&self) -> bool {
88 self.contents.is_empty()
89 }
90}
91
92impl<'a> Index<&'a str> for EdgeParameters {
94 type Output = FieldValue;
95
96 fn index(&self, index: &'a str) -> &Self::Output {
97 &self.contents[index]
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
106pub struct IRQueryComponent {
107 pub root: Vid,
109
110 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
111 pub vertices: BTreeMap<Vid, IRVertex>,
112
113 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
114 pub edges: BTreeMap<Eid, Arc<IREdge>>,
115
116 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
117 pub folds: BTreeMap<Eid, Arc<IRFold>>,
118
119 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
120 pub outputs: BTreeMap<Arc<str>, ContextField>,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub struct IRQuery {
126 pub root_name: Arc<str>,
127
128 #[serde(default, skip_serializing_if = "EdgeParameters::is_empty")]
129 pub root_parameters: EdgeParameters,
130
131 pub root_component: Arc<IRQueryComponent>,
132
133 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
134 pub variables: BTreeMap<Arc<str>, Type>,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct IREdge {
139 pub eid: Eid,
140 pub from_vid: Vid,
141 pub to_vid: Vid,
142 pub edge_name: Arc<str>,
143
144 #[serde(default, skip_serializing_if = "EdgeParameters::is_empty")]
145 pub parameters: EdgeParameters,
146
147 #[serde(default = "default_optional", skip_serializing_if = "is_false")]
151 pub optional: bool,
152
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 pub recursive: Option<Recursive>,
155}
156
157fn default_optional() -> bool {
158 false
159}
160
161fn is_false(b: &bool) -> bool {
162 !b
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct Recursive {
167 pub depth: NonZeroUsize,
168
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub coerce_to: Option<Arc<str>>,
171}
172
173impl Recursive {
174 pub fn new(depth: NonZeroUsize, coerce_to: Option<Arc<str>>) -> Self {
175 Self { depth, coerce_to }
176 }
177}
178
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
182pub struct IRVertex {
183 pub vid: Vid,
184
185 pub type_name: Arc<str>,
187
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub coerced_from_type: Option<Arc<str>>,
190
191 #[serde(default, skip_serializing_if = "Vec::is_empty")]
192 pub filters: Vec<Operation<LocalField, Argument>>,
193}
194
195#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub struct IRFold {
197 pub eid: Eid,
198 pub from_vid: Vid,
199 pub to_vid: Vid,
200 pub edge_name: Arc<str>,
201
202 #[serde(default, skip_serializing_if = "EdgeParameters::is_empty")]
203 pub parameters: EdgeParameters,
204
205 pub component: Arc<IRQueryComponent>,
206
207 #[serde(default, skip_serializing_if = "Vec::is_empty")]
210 pub imported_tags: Vec<FieldRef>,
211
212 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
213 pub fold_specific_outputs: BTreeMap<Arc<str>, FoldSpecificFieldKind>,
214
215 #[serde(default, skip_serializing_if = "Vec::is_empty")]
216 pub post_filters: Vec<Operation<FoldSpecificFieldKind, Argument>>,
217}
218
219#[non_exhaustive]
220#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
221pub enum FoldSpecificFieldKind {
222 Count, }
224
225static NON_NULL_INT_TYPE: OnceLock<Type> = OnceLock::new();
226
227impl FoldSpecificFieldKind {
228 pub fn field_type(&self) -> &Type {
229 match self {
230 Self::Count => NON_NULL_INT_TYPE.get_or_init(|| Type::new_named_type("Int", false)),
231 }
232 }
233
234 pub fn field_name(&self) -> &str {
235 match self {
236 FoldSpecificFieldKind::Count => "@fold.count",
237 }
238 }
239
240 pub fn transform_suffix(&self) -> &str {
241 match self {
242 FoldSpecificFieldKind::Count => "count",
243 }
244 }
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
248pub struct FoldSpecificField {
249 pub fold_eid: Eid,
251
252 pub fold_root_vid: Vid,
255
256 pub kind: FoldSpecificFieldKind,
257}
258
259#[non_exhaustive]
260#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
261pub enum TransformationKind {
262 Count,
263}
264
265#[non_exhaustive]
266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267pub enum FieldRef {
268 ContextField(ContextField),
269 FoldSpecificField(FoldSpecificField),
270}
271
272impl Ord for FieldRef {
273 fn cmp(&self, other: &Self) -> Ordering {
274 match (self, other) {
275 (FieldRef::ContextField(f1), FieldRef::ContextField(f2)) => f1
276 .vertex_id
277 .cmp(&f2.vertex_id)
278 .then(f1.field_name.as_ref().cmp(f2.field_name.as_ref())),
279 (FieldRef::ContextField(_), FieldRef::FoldSpecificField(_)) => Ordering::Less,
280 (FieldRef::FoldSpecificField(_), FieldRef::ContextField(_)) => Ordering::Greater,
281 (FieldRef::FoldSpecificField(f1), FieldRef::FoldSpecificField(f2)) => {
282 f1.fold_eid.cmp(&f2.fold_eid).then(f1.kind.cmp(&f2.kind))
283 }
284 }
285 }
286}
287
288impl PartialOrd for FieldRef {
289 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
290 Some(self.cmp(other))
291 }
292}
293
294impl From<ContextField> for FieldRef {
295 fn from(c: ContextField) -> Self {
296 Self::ContextField(c)
297 }
298}
299
300impl From<FoldSpecificField> for FieldRef {
301 fn from(f: FoldSpecificField) -> Self {
302 Self::FoldSpecificField(f)
303 }
304}
305
306impl FieldRef {
307 pub fn field_type(&self) -> &Type {
308 match self {
309 FieldRef::ContextField(c) => &c.field_type,
310 FieldRef::FoldSpecificField(f) => f.kind.field_type(),
311 }
312 }
313
314 pub fn field_name(&self) -> &str {
315 match self {
316 FieldRef::ContextField(c) => c.field_name.as_ref(),
317 FieldRef::FoldSpecificField(f) => f.kind.field_name(),
318 }
319 }
320
321 pub fn defined_at(&self) -> Vid {
323 match self {
324 FieldRef::ContextField(c) => c.vertex_id,
325 FieldRef::FoldSpecificField(f) => f.fold_root_vid,
326 }
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
331pub enum Argument {
332 Tag(FieldRef),
333 Variable(VariableRef),
334}
335
336impl Argument {
337 pub(crate) fn as_tag(&self) -> Option<&FieldRef> {
338 match self {
339 Argument::Tag(t) => Some(t),
340 Argument::Variable(_) => None,
341 }
342 }
343
344 pub(crate) fn evaluate_statically<'a>(
345 &self,
346 query_variables: &'a BTreeMap<Arc<str>, FieldValue>,
347 ) -> Option<Cow<'a, FieldValue>> {
348 match self {
349 Argument::Tag(..) => None,
350 Argument::Variable(var) => {
351 Some(Cow::Borrowed(&query_variables[var.variable_name.as_ref()]))
352 }
353 }
354 }
355}
356
357#[non_exhaustive]
365#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
366pub enum Operation<LeftT, RightT>
367where
368 LeftT: Debug + Clone + PartialEq + Eq,
369 RightT: Debug + Clone + PartialEq + Eq,
370{
371 IsNull(LeftT),
372 IsNotNull(LeftT),
373 Equals(LeftT, RightT),
374 NotEquals(LeftT, RightT),
375 LessThan(LeftT, RightT),
376 LessThanOrEqual(LeftT, RightT),
377 GreaterThan(LeftT, RightT),
378 GreaterThanOrEqual(LeftT, RightT),
379 Contains(LeftT, RightT),
380 NotContains(LeftT, RightT),
381 OneOf(LeftT, RightT),
382 NotOneOf(LeftT, RightT),
383 HasPrefix(LeftT, RightT),
384 NotHasPrefix(LeftT, RightT),
385 HasSuffix(LeftT, RightT),
386 NotHasSuffix(LeftT, RightT),
387 HasSubstring(LeftT, RightT),
388 NotHasSubstring(LeftT, RightT),
389 RegexMatches(LeftT, RightT),
390 NotRegexMatches(LeftT, RightT),
391}
392
393impl<LeftT, RightT> Operation<LeftT, RightT>
394where
395 LeftT: Debug + Clone + PartialEq + Eq,
396 RightT: Debug + Clone + PartialEq + Eq,
397{
398 pub(crate) fn left(&self) -> &LeftT {
399 match self {
400 Operation::IsNull(left) => left,
401 Operation::IsNotNull(left) => left,
402 Operation::Equals(left, _) => left,
403 Operation::NotEquals(left, _) => left,
404 Operation::LessThan(left, _) => left,
405 Operation::LessThanOrEqual(left, _) => left,
406 Operation::GreaterThan(left, _) => left,
407 Operation::GreaterThanOrEqual(left, _) => left,
408 Operation::Contains(left, _) => left,
409 Operation::NotContains(left, _) => left,
410 Operation::OneOf(left, _) => left,
411 Operation::NotOneOf(left, _) => left,
412 Operation::HasPrefix(left, _) => left,
413 Operation::NotHasPrefix(left, _) => left,
414 Operation::HasSuffix(left, _) => left,
415 Operation::NotHasSuffix(left, _) => left,
416 Operation::HasSubstring(left, _) => left,
417 Operation::NotHasSubstring(left, _) => left,
418 Operation::RegexMatches(left, _) => left,
419 Operation::NotRegexMatches(left, _) => left,
420 }
421 }
422
423 pub(crate) fn right(&self) -> Option<&RightT> {
424 match self {
425 Operation::IsNull(_) | Operation::IsNotNull(_) => None,
426 Operation::Equals(_, right) => Some(right),
427 Operation::NotEquals(_, right) => Some(right),
428 Operation::LessThan(_, right) => Some(right),
429 Operation::LessThanOrEqual(_, right) => Some(right),
430 Operation::GreaterThan(_, right) => Some(right),
431 Operation::GreaterThanOrEqual(_, right) => Some(right),
432 Operation::Contains(_, right) => Some(right),
433 Operation::NotContains(_, right) => Some(right),
434 Operation::OneOf(_, right) => Some(right),
435 Operation::NotOneOf(_, right) => Some(right),
436 Operation::HasPrefix(_, right) => Some(right),
437 Operation::NotHasPrefix(_, right) => Some(right),
438 Operation::HasSuffix(_, right) => Some(right),
439 Operation::NotHasSuffix(_, right) => Some(right),
440 Operation::HasSubstring(_, right) => Some(right),
441 Operation::NotHasSubstring(_, right) => Some(right),
442 Operation::RegexMatches(_, right) => Some(right),
443 Operation::NotRegexMatches(_, right) => Some(right),
444 }
445 }
446
447 pub(crate) fn operation_name(&self) -> &'static str {
449 match self {
450 Operation::IsNull(..) => "is_null",
451 Operation::IsNotNull(..) => "is_not_null",
452 Operation::Equals(..) => "=",
453 Operation::NotEquals(..) => "!=",
454 Operation::LessThan(..) => "<",
455 Operation::LessThanOrEqual(..) => "<=",
456 Operation::GreaterThan(..) => ">",
457 Operation::GreaterThanOrEqual(..) => ">=",
458 Operation::Contains(..) => "contains",
459 Operation::NotContains(..) => "not_contains",
460 Operation::OneOf(..) => "one_of",
461 Operation::NotOneOf(..) => "not_one_of",
462 Operation::HasPrefix(..) => "has_prefix",
463 Operation::NotHasPrefix(..) => "not_has_prefix",
464 Operation::HasSuffix(..) => "has_suffix",
465 Operation::NotHasSuffix(..) => "not_has_suffix",
466 Operation::HasSubstring(..) => "has_substring",
467 Operation::NotHasSubstring(..) => "not_has_substring",
468 Operation::RegexMatches(..) => "regex",
469 Operation::NotRegexMatches(..) => "not_regex",
470 }
471 }
472
473 pub(crate) fn map<'a, LeftF, LeftOutT, RightF, RightOutT>(
474 &'a self,
475 map_left: LeftF,
476 map_right: RightF,
477 ) -> Operation<LeftOutT, RightOutT>
478 where
479 LeftOutT: Debug + Clone + PartialEq + Eq,
480 RightOutT: Debug + Clone + PartialEq + Eq,
481 LeftF: FnOnce(&'a LeftT) -> LeftOutT,
482 RightF: FnOnce(&'a RightT) -> RightOutT,
483 {
484 match self {
485 Operation::IsNull(left) => Operation::IsNull(map_left(left)),
486 Operation::IsNotNull(left) => Operation::IsNotNull(map_left(left)),
487 Operation::Equals(left, right) => Operation::Equals(map_left(left), map_right(right)),
488 Operation::NotEquals(left, right) => {
489 Operation::NotEquals(map_left(left), map_right(right))
490 }
491 Operation::LessThan(left, right) => {
492 Operation::LessThan(map_left(left), map_right(right))
493 }
494 Operation::LessThanOrEqual(left, right) => {
495 Operation::LessThanOrEqual(map_left(left), map_right(right))
496 }
497 Operation::GreaterThan(left, right) => {
498 Operation::GreaterThan(map_left(left), map_right(right))
499 }
500 Operation::GreaterThanOrEqual(left, right) => {
501 Operation::GreaterThanOrEqual(map_left(left), map_right(right))
502 }
503 Operation::Contains(left, right) => {
504 Operation::Contains(map_left(left), map_right(right))
505 }
506 Operation::NotContains(left, right) => {
507 Operation::NotContains(map_left(left), map_right(right))
508 }
509 Operation::OneOf(left, right) => Operation::OneOf(map_left(left), map_right(right)),
510 Operation::NotOneOf(left, right) => {
511 Operation::NotOneOf(map_left(left), map_right(right))
512 }
513 Operation::HasPrefix(left, right) => {
514 Operation::HasPrefix(map_left(left), map_right(right))
515 }
516 Operation::NotHasPrefix(left, right) => {
517 Operation::NotHasPrefix(map_left(left), map_right(right))
518 }
519 Operation::HasSuffix(left, right) => {
520 Operation::HasSuffix(map_left(left), map_right(right))
521 }
522 Operation::NotHasSuffix(left, right) => {
523 Operation::NotHasSuffix(map_left(left), map_right(right))
524 }
525 Operation::HasSubstring(left, right) => {
526 Operation::HasSubstring(map_left(left), map_right(right))
527 }
528 Operation::NotHasSubstring(left, right) => {
529 Operation::NotHasSubstring(map_left(left), map_right(right))
530 }
531 Operation::RegexMatches(left, right) => {
532 Operation::RegexMatches(map_left(left), map_right(right))
533 }
534 Operation::NotRegexMatches(left, right) => {
535 Operation::NotRegexMatches(map_left(left), map_right(right))
536 }
537 }
538 }
539
540 pub(crate) fn try_map<LeftF, LeftOutT, RightF, RightOutT, Err>(
541 &self,
542 map_left: LeftF,
543 map_right: RightF,
544 ) -> Result<Operation<LeftOutT, RightOutT>, Err>
545 where
546 LeftOutT: Debug + Clone + PartialEq + Eq,
547 RightOutT: Debug + Clone + PartialEq + Eq,
548 LeftF: FnOnce(&LeftT) -> Result<LeftOutT, Err>,
549 RightF: FnOnce(&RightT) -> Result<RightOutT, Err>,
550 {
551 Ok(match self {
552 Operation::IsNull(left) => Operation::IsNull(map_left(left)?),
553 Operation::IsNotNull(left) => Operation::IsNotNull(map_left(left)?),
554 Operation::Equals(left, right) => Operation::Equals(map_left(left)?, map_right(right)?),
555 Operation::NotEquals(left, right) => {
556 Operation::NotEquals(map_left(left)?, map_right(right)?)
557 }
558 Operation::LessThan(left, right) => {
559 Operation::LessThan(map_left(left)?, map_right(right)?)
560 }
561 Operation::LessThanOrEqual(left, right) => {
562 Operation::LessThanOrEqual(map_left(left)?, map_right(right)?)
563 }
564 Operation::GreaterThan(left, right) => {
565 Operation::GreaterThan(map_left(left)?, map_right(right)?)
566 }
567 Operation::GreaterThanOrEqual(left, right) => {
568 Operation::GreaterThanOrEqual(map_left(left)?, map_right(right)?)
569 }
570 Operation::Contains(left, right) => {
571 Operation::Contains(map_left(left)?, map_right(right)?)
572 }
573 Operation::NotContains(left, right) => {
574 Operation::NotContains(map_left(left)?, map_right(right)?)
575 }
576 Operation::OneOf(left, right) => Operation::OneOf(map_left(left)?, map_right(right)?),
577 Operation::NotOneOf(left, right) => {
578 Operation::NotOneOf(map_left(left)?, map_right(right)?)
579 }
580 Operation::HasPrefix(left, right) => {
581 Operation::HasPrefix(map_left(left)?, map_right(right)?)
582 }
583 Operation::NotHasPrefix(left, right) => {
584 Operation::NotHasPrefix(map_left(left)?, map_right(right)?)
585 }
586 Operation::HasSuffix(left, right) => {
587 Operation::HasSuffix(map_left(left)?, map_right(right)?)
588 }
589 Operation::NotHasSuffix(left, right) => {
590 Operation::NotHasSuffix(map_left(left)?, map_right(right)?)
591 }
592 Operation::HasSubstring(left, right) => {
593 Operation::HasSubstring(map_left(left)?, map_right(right)?)
594 }
595 Operation::NotHasSubstring(left, right) => {
596 Operation::NotHasSubstring(map_left(left)?, map_right(right)?)
597 }
598 Operation::RegexMatches(left, right) => {
599 Operation::RegexMatches(map_left(left)?, map_right(right)?)
600 }
601 Operation::NotRegexMatches(left, right) => {
602 Operation::NotRegexMatches(map_left(left)?, map_right(right)?)
603 }
604 })
605 }
606}
607
608#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
609pub struct ContextField {
610 pub vertex_id: Vid,
611
612 pub field_name: Arc<str>,
613
614 pub field_type: Type,
615}
616
617#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
618pub struct LocalField {
619 pub field_name: Arc<str>,
620
621 pub field_type: Type,
622}
623
624#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
625pub struct VariableRef {
626 pub variable_name: Arc<str>,
627
628 pub variable_type: Type,
629}
630
631#[cfg(test)]
632mod tests {
633 use std::sync::Arc;
634
635 use super::FieldValue;
636
637 fn serialize_then_deserialize(value: &FieldValue) -> FieldValue {
638 ron::from_str(ron::to_string(value).unwrap().as_str()).unwrap()
639 }
640
641 #[test]
642 fn serialize_then_deserialize_enum() {
643 let value = FieldValue::Enum("foo".into());
644 let deserialized: FieldValue = serialize_then_deserialize(&value);
645 assert_eq!(value, deserialized, "Serialized as: {}", ron::to_string(&value).unwrap());
646 }
647
648 #[test]
649 fn serialize_then_deserialize_list() {
650 let value = FieldValue::List(Arc::new([
651 FieldValue::Int64(1),
652 FieldValue::Int64(2),
653 FieldValue::String("foo".into()),
654 ]));
655 let deserialized: FieldValue = serialize_then_deserialize(&value);
656 assert_eq!(value, deserialized, "Serialized as: {}", ron::to_string(&value).unwrap());
657 }
658
659 #[test]
660 fn serialize_then_deserialize_float() {
661 let value = FieldValue::Float64(1.0);
662 let deserialized: FieldValue = serialize_then_deserialize(&value);
663 assert_eq!(value, deserialized, "Serialized as: {}", ron::to_string(&value).unwrap());
664 }
665
666 #[test]
667 fn serialize_then_deserialize_i64() {
668 let value = FieldValue::Int64(-123);
669 let deserialized: FieldValue = serialize_then_deserialize(&value);
670 assert_eq!(value, deserialized, "Serialized as: {}", ron::to_string(&value).unwrap());
671 }
672
673 #[test]
674 fn serialize_then_deserialize_u64() {
675 let value = FieldValue::Uint64((i64::MAX as u64) + 1);
676 let deserialized: FieldValue = serialize_then_deserialize(&value);
677 assert_eq!(value, deserialized, "Serialized as: {}", ron::to_string(&value).unwrap());
678 }
679}