1use std::collections::BTreeMap;
10use std::sync::Arc;
11
12use crossbeam::channel::{bounded, Receiver, Sender};
13#[allow(unused_imports)]
14use either::{Left, Right};
15#[cfg(feature = "graph-algo")]
16use graph::prelude::{CsrLayout, DirectedCsrGraph, GraphBuilder};
17use itertools::Itertools;
18use lazy_static::lazy_static;
19use miette::IntoDiagnostic;
20#[allow(unused_imports)]
21use miette::{bail, ensure, Diagnostic, Report, Result};
22use smartstring::{LazyCompact, SmartString};
23use thiserror::Error;
24
25use crate::data::expr::Expr;
26use crate::data::program::{
27 FixedRuleOptionNotFoundError, MagicFixedRuleApply, MagicFixedRuleRuleArg, MagicSymbol,
28 WrongFixedRuleOptionError,
29};
30use crate::data::symb::Symbol;
31use crate::data::tuple::TupleIter;
32use crate::data::value::DataValue;
33#[cfg(feature = "graph-algo")]
34use crate::fixed_rule::algos::*;
35use crate::fixed_rule::utilities::*;
36use crate::parse::SourceSpan;
37use crate::runtime::db::Poison;
38use crate::runtime::temp_store::{EpochStore, RegularTempStore};
39use crate::runtime::transact::SessionTx;
40use crate::NamedRows;
41
42#[cfg(feature = "graph-algo")]
43pub(crate) mod algos;
44pub(crate) mod utilities;
45
46pub struct FixedRulePayload<'a, 'b> {
48 pub(crate) manifest: &'a MagicFixedRuleApply,
49 pub(crate) stores: &'a BTreeMap<MagicSymbol, EpochStore>,
50 pub(crate) tx: &'a SessionTx<'b>,
51}
52
53#[derive(Copy, Clone)]
55pub struct FixedRuleInputRelation<'a, 'b> {
56 arg_manifest: &'a MagicFixedRuleRuleArg,
57 stores: &'a BTreeMap<MagicSymbol, EpochStore>,
58 tx: &'a SessionTx<'b>,
59}
60
61impl<'a, 'b> FixedRuleInputRelation<'a, 'b> {
62 pub fn arity(&self) -> Result<usize> {
64 self.arg_manifest.arity(self.tx, self.stores)
65 }
66 pub fn ensure_min_len(self, len: usize) -> Result<Self> {
68 #[derive(Error, Diagnostic, Debug)]
69 #[error("Input relation to algorithm has insufficient arity")]
70 #[diagnostic(help("Arity should be at least {0} but is {1}"))]
71 #[diagnostic(code(algo::input_relation_bad_arity))]
72 struct InputRelationArityError(usize, usize, #[label] SourceSpan);
73
74 let arity = self.arg_manifest.arity(self.tx, self.stores)?;
75 ensure!(
76 arity >= len,
77 InputRelationArityError(len, arity, self.arg_manifest.span())
78 );
79 Ok(self)
80 }
81 pub fn get_binding_map(&self, offset: usize) -> BTreeMap<Symbol, usize> {
83 self.arg_manifest.get_binding_map(offset)
84 }
85 pub fn iter(&self) -> Result<TupleIter<'a>> {
87 Ok(match &self.arg_manifest {
88 MagicFixedRuleRuleArg::InMem { name, .. } => {
89 let store = self.stores.get(name).ok_or_else(|| {
90 RuleNotFoundError(name.symbol().to_string(), name.symbol().span)
91 })?;
92 Box::new(store.all_iter().map(|t| Ok(t.into_tuple())))
93 }
94 MagicFixedRuleRuleArg::Stored { name, valid_at, .. } => {
95 let relation = self.tx.get_relation(name, false)?;
96 if let Some(valid_at) = valid_at {
97 Box::new(relation.skip_scan_all(self.tx, *valid_at))
98 } else {
99 Box::new(relation.scan_all(self.tx))
100 }
101 }
102 })
103 }
104 pub fn prefix_iter(&self, prefix: &DataValue) -> Result<TupleIter<'_>> {
106 Ok(match self.arg_manifest {
107 MagicFixedRuleRuleArg::InMem { name, .. } => {
108 let store = self.stores.get(name).ok_or_else(|| {
109 RuleNotFoundError(name.symbol().to_string(), name.symbol().span)
110 })?;
111 let t = vec![prefix.clone()];
112 Box::new(store.prefix_iter(&t).map(|t| Ok(t.into_tuple())))
113 }
114 MagicFixedRuleRuleArg::Stored { name, valid_at, .. } => {
115 let relation = self.tx.get_relation(name, false)?;
116 let t = vec![prefix.clone()];
117 if let Some(valid_at) = valid_at {
118 Box::new(relation.skip_scan_prefix(self.tx, &t, *valid_at))
119 } else {
120 Box::new(relation.scan_prefix(self.tx, &t))
121 }
122 }
123 })
124 }
125 pub fn span(&self) -> SourceSpan {
127 self.arg_manifest.span()
128 }
129 #[cfg(feature = "graph-algo")]
136 pub fn as_directed_graph(
137 &self,
138 undirected: bool,
139 ) -> Result<(
140 DirectedCsrGraph<u32>,
141 Vec<DataValue>,
142 BTreeMap<DataValue, u32>,
143 )> {
144 let mut indices: Vec<DataValue> = vec![];
145 let mut inv_indices: BTreeMap<DataValue, u32> = Default::default();
146 let mut error: Option<Report> = None;
147 let it = self.iter()?.filter_map(|r_tuple| match r_tuple {
148 Ok(tuple) => {
149 let mut tuple = tuple.into_iter();
150 let from = match tuple.next() {
151 None => {
152 error = Some(NotAnEdgeError(self.span()).into());
153 return None;
154 }
155 Some(f) => f,
156 };
157 let to = match tuple.next() {
158 None => {
159 error = Some(NotAnEdgeError(self.span()).into());
160 return None;
161 }
162 Some(f) => f,
163 };
164 let from_idx = if let Some(idx) = inv_indices.get(&from) {
165 *idx
166 } else {
167 let idx = indices.len() as u32;
168 inv_indices.insert(from.clone(), idx);
169 indices.push(from.clone());
170 idx
171 };
172 let to_idx = if let Some(idx) = inv_indices.get(&to) {
173 *idx
174 } else {
175 let idx = indices.len() as u32;
176 inv_indices.insert(to.clone(), idx);
177 indices.push(to.clone());
178 idx
179 };
180 Some((from_idx, to_idx))
181 }
182 Err(err) => {
183 error = Some(err);
184 None
185 }
186 });
187 let it = if undirected {
188 Right(it.flat_map(|(f, t)| [(f, t), (t, f)]))
189 } else {
190 Left(it)
191 };
192 let graph: DirectedCsrGraph<u32> = GraphBuilder::new()
193 .csr_layout(CsrLayout::Sorted)
194 .edges(it)
195 .build();
196 if let Some(err) = error {
197 bail!(err)
198 }
199 Ok((graph, indices, inv_indices))
200 }
201 #[cfg(feature = "graph-algo")]
208 pub fn as_directed_weighted_graph(
209 &self,
210 undirected: bool,
211 allow_negative_weights: bool,
212 ) -> Result<(
213 DirectedCsrGraph<u32, (), f32>,
214 Vec<DataValue>,
215 BTreeMap<DataValue, u32>,
216 )> {
217 let mut indices: Vec<DataValue> = vec![];
218 let mut inv_indices: BTreeMap<DataValue, u32> = Default::default();
219 let mut error: Option<Report> = None;
220 let it = self.iter()?.filter_map(|r_tuple| match r_tuple {
221 Ok(tuple) => {
222 let mut tuple = tuple.into_iter();
223 let from = match tuple.next() {
224 None => {
225 error = Some(NotAnEdgeError(self.span()).into());
226 return None;
227 }
228 Some(f) => f,
229 };
230 let to = match tuple.next() {
231 None => {
232 error = Some(NotAnEdgeError(self.span()).into());
233 return None;
234 }
235 Some(f) => f,
236 };
237 let from_idx = if let Some(idx) = inv_indices.get(&from) {
238 *idx
239 } else {
240 let idx = indices.len() as u32;
241 inv_indices.insert(from.clone(), idx);
242 indices.push(from.clone());
243 idx
244 };
245 let to_idx = if let Some(idx) = inv_indices.get(&to) {
246 *idx
247 } else {
248 let idx = indices.len() as u32;
249 inv_indices.insert(to.clone(), idx);
250 indices.push(to.clone());
251 idx
252 };
253
254 let weight = match tuple.next() {
255 None => 1.0,
256 Some(d) => match d.get_float() {
257 Some(f) => {
258 if !f.is_finite() {
259 error = Some(
260 BadEdgeWeightError(
261 d,
262 self.arg_manifest
263 .bindings()
264 .get(2)
265 .map(|s| s.span)
266 .unwrap_or_else(|| self.span()),
267 )
268 .into(),
269 );
270 return None;
271 };
272
273 if f < 0. && !allow_negative_weights {
274 error = Some(
275 BadEdgeWeightError(
276 d,
277 self.arg_manifest
278 .bindings()
279 .get(2)
280 .map(|s| s.span)
281 .unwrap_or_else(|| self.span()),
282 )
283 .into(),
284 );
285 return None;
286 }
287 f
288 }
289 None => {
290 error = Some(
291 BadEdgeWeightError(
292 d,
293 self.arg_manifest
294 .bindings()
295 .get(2)
296 .map(|s| s.span)
297 .unwrap_or_else(|| self.span()),
298 )
299 .into(),
300 );
301 return None;
302 }
303 },
304 };
305
306 Some((from_idx, to_idx, weight as f32))
307 }
308 Err(err) => {
309 error = Some(err);
310 None
311 }
312 });
313 let it = if undirected {
314 Right(it.flat_map(|(f, t, w)| [(f, t, w), (t, f, w)]))
315 } else {
316 Left(it)
317 };
318 let graph: DirectedCsrGraph<u32, (), f32> = GraphBuilder::new()
319 .csr_layout(CsrLayout::Sorted)
320 .edges_with_values(it)
321 .build();
322
323 if let Some(err) = error {
324 bail!(err)
325 }
326
327 Ok((graph, indices, inv_indices))
328 }
329}
330
331impl<'a, 'b> FixedRulePayload<'a, 'b> {
332 pub fn inputs_count(&self) -> usize {
334 self.manifest.relations_count()
335 }
336 pub fn get_input(&self, idx: usize) -> Result<FixedRuleInputRelation<'a, 'b>> {
338 let arg_manifest = self.manifest.relation(idx)?;
339 Ok(FixedRuleInputRelation {
340 arg_manifest,
341 stores: self.stores,
342 tx: self.tx,
343 })
344 }
345 pub fn name(&self) -> &str {
347 &self.manifest.fixed_handle.name
348 }
349 pub fn span(&self) -> SourceSpan {
351 self.manifest.span
352 }
353 pub fn expr_option(&self, name: &str, default: Option<Expr>) -> Result<Expr> {
355 match self.manifest.options.get(name) {
356 Some(ex) => Ok(ex.clone()),
357 None => match default {
358 Some(ex) => Ok(ex),
359 None => Err(FixedRuleOptionNotFoundError {
360 name: name.to_string(),
361 span: self.manifest.span,
362 rule_name: self.manifest.fixed_handle.name.to_string(),
363 }
364 .into()),
365 },
366 }
367 }
368
369 pub fn string_option(
371 &self,
372 name: &str,
373 default: Option<&str>,
374 ) -> Result<SmartString<LazyCompact>> {
375 match self.manifest.options.get(name) {
376 Some(ex) => match ex.clone().eval_to_const()? {
377 DataValue::Str(s) => Ok(s),
378 _ => Err(WrongFixedRuleOptionError {
379 name: name.to_string(),
380 span: ex.span(),
381 rule_name: self.manifest.fixed_handle.name.to_string(),
382 help: "a string is required".to_string(),
383 }
384 .into()),
385 },
386 None => match default {
387 None => Err(FixedRuleOptionNotFoundError {
388 name: name.to_string(),
389 span: self.manifest.span,
390 rule_name: self.manifest.fixed_handle.name.to_string(),
391 }
392 .into()),
393 Some(s) => Ok(SmartString::from(s)),
394 },
395 }
396 }
397
398 pub fn option_span(&self, name: &str) -> Result<SourceSpan> {
400 match self.manifest.options.get(name) {
401 None => Err(FixedRuleOptionNotFoundError {
402 name: name.to_string(),
403 span: self.manifest.span,
404 rule_name: self.manifest.fixed_handle.name.to_string(),
405 }
406 .into()),
407 Some(v) => Ok(v.span()),
408 }
409 }
410 pub fn integer_option(&self, name: &str, default: Option<i64>) -> Result<i64> {
412 match self.manifest.options.get(name) {
413 Some(v) => match v.clone().eval_to_const() {
414 Ok(DataValue::Num(n)) => match n.get_int() {
415 Some(i) => Ok(i),
416 None => Err(FixedRuleOptionNotFoundError {
417 name: name.to_string(),
418 span: self.manifest.span,
419 rule_name: self.manifest.fixed_handle.name.to_string(),
420 }
421 .into()),
422 },
423 _ => Err(WrongFixedRuleOptionError {
424 name: name.to_string(),
425 span: v.span(),
426 rule_name: self.manifest.fixed_handle.name.to_string(),
427 help: "an integer is required".to_string(),
428 }
429 .into()),
430 },
431 None => match default {
432 Some(v) => Ok(v),
433 None => Err(FixedRuleOptionNotFoundError {
434 name: name.to_string(),
435 span: self.manifest.span,
436 rule_name: self.manifest.fixed_handle.name.to_string(),
437 }
438 .into()),
439 },
440 }
441 }
442 pub fn pos_integer_option(&self, name: &str, default: Option<usize>) -> Result<usize> {
444 let i = self.integer_option(name, default.map(|i| i as i64))?;
445 ensure!(
446 i > 0,
447 WrongFixedRuleOptionError {
448 name: name.to_string(),
449 span: self.option_span(name)?,
450 rule_name: self.manifest.fixed_handle.name.to_string(),
451 help: "a positive integer is required".to_string(),
452 }
453 );
454 Ok(i as usize)
455 }
456 pub fn non_neg_integer_option(&self, name: &str, default: Option<usize>) -> Result<usize> {
458 let i = self.integer_option(name, default.map(|i| i as i64))?;
459 ensure!(
460 i >= 0,
461 WrongFixedRuleOptionError {
462 name: name.to_string(),
463 span: self.option_span(name)?,
464 rule_name: self.manifest.fixed_handle.name.to_string(),
465 help: "a non-negative integer is required".to_string(),
466 }
467 );
468 Ok(i as usize)
469 }
470 pub fn float_option(&self, name: &str, default: Option<f64>) -> Result<f64> {
472 match self.manifest.options.get(name) {
473 Some(v) => match v.clone().eval_to_const() {
474 Ok(DataValue::Num(n)) => {
475 let f = n.get_float();
476 Ok(f)
477 }
478 _ => Err(WrongFixedRuleOptionError {
479 name: name.to_string(),
480 span: v.span(),
481 rule_name: self.manifest.fixed_handle.name.to_string(),
482 help: "a floating number is required".to_string(),
483 }
484 .into()),
485 },
486 None => match default {
487 Some(v) => Ok(v),
488 None => Err(FixedRuleOptionNotFoundError {
489 name: name.to_string(),
490 span: self.manifest.span,
491 rule_name: self.manifest.fixed_handle.name.to_string(),
492 }
493 .into()),
494 },
495 }
496 }
497 pub fn unit_interval_option(&self, name: &str, default: Option<f64>) -> Result<f64> {
499 let f = self.float_option(name, default)?;
500 ensure!(
501 (0. ..=1.).contains(&f),
502 WrongFixedRuleOptionError {
503 name: name.to_string(),
504 span: self.option_span(name)?,
505 rule_name: self.manifest.fixed_handle.name.to_string(),
506 help: "a number between 0. and 1. is required".to_string(),
507 }
508 );
509 Ok(f)
510 }
511 pub fn bool_option(&self, name: &str, default: Option<bool>) -> Result<bool> {
513 match self.manifest.options.get(name) {
514 Some(v) => match v.clone().eval_to_const() {
515 Ok(DataValue::Bool(b)) => Ok(b),
516 _ => Err(WrongFixedRuleOptionError {
517 name: name.to_string(),
518 span: v.span(),
519 rule_name: self.manifest.fixed_handle.name.to_string(),
520 help: "a boolean value is required".to_string(),
521 }
522 .into()),
523 },
524 None => match default {
525 Some(v) => Ok(v),
526 None => Err(FixedRuleOptionNotFoundError {
527 name: name.to_string(),
528 span: self.manifest.span,
529 rule_name: self.manifest.fixed_handle.name.to_string(),
530 }
531 .into()),
532 },
533 }
534 }
535}
536
537pub trait FixedRule: Send + Sync {
539 fn init_options(
544 &self,
545 _options: &mut BTreeMap<SmartString<LazyCompact>, Expr>,
546 _span: SourceSpan,
547 ) -> Result<()> {
548 Ok(())
549 }
550 fn arity(
553 &self,
554 options: &BTreeMap<SmartString<LazyCompact>, Expr>,
555 rule_head: &[Symbol],
556 span: SourceSpan,
557 ) -> Result<usize>;
558 fn run(
562 &self,
563 payload: FixedRulePayload<'_, '_>,
564 out: &'_ mut RegularTempStore,
565 poison: Poison,
566 ) -> Result<()>;
567}
568
569pub struct SimpleFixedRule {
572 return_arity: usize,
573 rule: Box<
574 dyn Fn(Vec<NamedRows>, BTreeMap<String, DataValue>) -> Result<NamedRows>
575 + Send
576 + Sync
577 + 'static,
578 >,
579}
580
581impl SimpleFixedRule {
582 pub fn new<R>(return_arity: usize, rule: R) -> Self
591 where
592 R: Fn(Vec<NamedRows>, BTreeMap<String, DataValue>) -> Result<NamedRows>
593 + Send
594 + Sync
595 + 'static,
596 {
597 Self {
598 return_arity,
599 rule: Box::new(rule),
600 }
601 }
602 pub fn rule_with_channel(
604 return_arity: usize,
605 ) -> (
606 Self,
607 Receiver<(
608 Vec<NamedRows>,
609 BTreeMap<String, DataValue>,
610 Sender<Result<NamedRows>>,
611 )>,
612 ) {
613 let (db2app_sender, db2app_receiver) = bounded(0);
614 (
615 Self {
616 return_arity,
617 rule: Box::new(move |inputs, options| -> Result<NamedRows> {
618 let (app2db_sender, app2db_receiver) = bounded(0);
619 db2app_sender
620 .send((inputs, options, app2db_sender))
621 .into_diagnostic()?;
622 app2db_receiver.recv().into_diagnostic()?
623 }),
624 },
625 db2app_receiver,
626 )
627 }
628}
629
630impl FixedRule for SimpleFixedRule {
631 fn arity(
632 &self,
633 _options: &BTreeMap<SmartString<LazyCompact>, Expr>,
634 _rule_head: &[Symbol],
635 _span: SourceSpan,
636 ) -> Result<usize> {
637 Ok(self.return_arity)
638 }
639
640 fn run(
641 &self,
642 payload: FixedRulePayload<'_, '_>,
643 out: &'_ mut RegularTempStore,
644 _poison: Poison,
645 ) -> Result<()> {
646 let options: BTreeMap<_, _> = payload
647 .manifest
648 .options
649 .iter()
650 .map(|(k, v)| -> Result<_> {
651 let val = v.clone().eval_to_const()?;
652 Ok((k.to_string(), val))
653 })
654 .try_collect()?;
655 let input_arity = payload.manifest.rule_args.len();
656 let inputs: Vec<_> = (0..input_arity)
657 .map(|i| -> Result<_> {
658 let input = payload.get_input(i).unwrap();
659 let rows: Vec<_> = input.iter()?.try_collect()?;
660 let mut headers = input
661 .arg_manifest
662 .bindings()
663 .iter()
664 .map(|s| s.name.to_string())
665 .collect_vec();
666 let l = headers.len();
667 let m = input.arg_manifest.arity(payload.tx, payload.stores)?;
668 for i in l..m {
669 headers.push(format!("_{i}"));
670 }
671 Ok(NamedRows::new(headers, rows))
672 })
673 .try_collect()?;
674 let results: NamedRows = (self.rule)(inputs, options)?;
675 for row in results.rows {
676 #[derive(Debug, Error, Diagnostic)]
677 #[error("arity mismatch: expect {1}, got {2}")]
678 #[diagnostic(code(parser::simple_fixed_rule_arity_mismatch))]
679 struct ArityMismatch(#[label] SourceSpan, usize, usize);
680
681 ensure!(
682 row.len() == self.return_arity,
683 ArityMismatch(payload.span(), self.return_arity, row.len())
684 );
685 out.put(row);
686 }
687 Ok(())
688 }
689}
690
691#[derive(Debug, Error, Diagnostic)]
692#[error("Cannot determine arity for algo {0} since {1}")]
693#[diagnostic(code(parser::no_algo_arity))]
694pub(crate) struct CannotDetermineArity(
695 pub(crate) String,
696 pub(crate) String,
697 #[label] pub(crate) SourceSpan,
698);
699
700#[derive(Clone, Debug)]
701pub(crate) struct FixedRuleHandle {
702 pub(crate) name: Symbol,
703}
704
705lazy_static! {
706 pub(crate) static ref DEFAULT_FIXED_RULES: BTreeMap<String, Arc<Box<dyn FixedRule>>> = {
707 BTreeMap::from([
708 #[cfg(feature = "graph-algo")]
709 (
710 "ClusteringCoefficients".to_string(),
711 Arc::<Box<dyn FixedRule>>::new(Box::new(ClusteringCoefficients)),
712 ),
713 #[cfg(feature = "graph-algo")]
714 (
715 "DegreeCentrality".to_string(),
716 Arc::<Box<dyn FixedRule>>::new(Box::new(DegreeCentrality)),
717 ),
718 #[cfg(feature = "graph-algo")]
719 (
720 "ClosenessCentrality".to_string(),
721 Arc::<Box<dyn FixedRule>>::new(Box::new(ClosenessCentrality)),
722 ),
723 #[cfg(feature = "graph-algo")]
724 (
725 "BetweennessCentrality".to_string(),
726 Arc::<Box<dyn FixedRule>>::new(Box::new(BetweennessCentrality)),
727 ),
728 #[cfg(feature = "graph-algo")]
729 (
730 "DepthFirstSearch".to_string(),
731 Arc::<Box<dyn FixedRule>>::new(Box::new(Dfs)),
732 ),
733 #[cfg(feature = "graph-algo")]
734 (
735 "DFS".to_string(),
736 Arc::<Box<dyn FixedRule>>::new(Box::new(Dfs)),
737 ),
738 #[cfg(feature = "graph-algo")]
739 (
740 "BreadthFirstSearch".to_string(),
741 Arc::<Box<dyn FixedRule>>::new(Box::new(Bfs)),
742 ),
743 #[cfg(feature = "graph-algo")]
744 (
745 "BFS".to_string(),
746 Arc::<Box<dyn FixedRule>>::new(Box::new(Bfs)),
747 ),
748 #[cfg(feature = "graph-algo")]
749 (
750 "ShortestPathBFS".to_string(),
751 Arc::<Box<dyn FixedRule>>::new(Box::new(ShortestPathBFS)),
752 ),
753 #[cfg(feature = "graph-algo")]
754 (
755 "ShortestPathDijkstra".to_string(),
756 Arc::<Box<dyn FixedRule>>::new(Box::new(ShortestPathDijkstra)),
757 ),
758 #[cfg(feature = "graph-algo")]
759 (
760 "ShortestPathAStar".to_string(),
761 Arc::<Box<dyn FixedRule>>::new(Box::new(ShortestPathAStar)),
762 ),
763 #[cfg(feature = "graph-algo")]
764 (
765 "KShortestPathYen".to_string(),
766 Arc::<Box<dyn FixedRule>>::new(Box::new(KShortestPathYen)),
767 ),
768 #[cfg(feature = "graph-algo")]
769 (
770 "MinimumSpanningTreePrim".to_string(),
771 Arc::<Box<dyn FixedRule>>::new(Box::new(MinimumSpanningTreePrim)),
772 ),
773 #[cfg(feature = "graph-algo")]
774 (
775 "MinimumSpanningForestKruskal".to_string(),
776 Arc::<Box<dyn FixedRule>>::new(Box::new(MinimumSpanningForestKruskal)),
777 ),
778 #[cfg(feature = "graph-algo")]
779 (
780 "TopSort".to_string(),
781 Arc::<Box<dyn FixedRule>>::new(Box::new(TopSort)),
782 ),
783 #[cfg(feature = "graph-algo")]
784 (
785 "ConnectedComponents".to_string(),
786 Arc::<Box<dyn FixedRule>>::new(Box::new(StronglyConnectedComponent::new(false))),
787 ),
788 #[cfg(feature = "graph-algo")]
789 (
790 "StronglyConnectedComponents".to_string(),
791 Arc::<Box<dyn FixedRule>>::new(Box::new(StronglyConnectedComponent::new(true))),
792 ),
793 #[cfg(feature = "graph-algo")]
794 (
795 "SCC".to_string(),
796 Arc::<Box<dyn FixedRule>>::new(Box::new(StronglyConnectedComponent::new(true))),
797 ),
798 #[cfg(feature = "graph-algo")]
799 (
800 "PageRank".to_string(),
801 Arc::<Box<dyn FixedRule>>::new(Box::new(PageRank)),
802 ),
803 #[cfg(feature = "graph-algo")]
804 (
805 "CommunityDetectionLouvain".to_string(),
806 Arc::<Box<dyn FixedRule>>::new(Box::new(CommunityDetectionLouvain)),
807 ),
808 #[cfg(feature = "graph-algo")]
809 (
810 "LabelPropagation".to_string(),
811 Arc::<Box<dyn FixedRule>>::new(Box::new(LabelPropagation)),
812 ),
813 #[cfg(feature = "graph-algo")]
814 (
815 "RandomWalk".to_string(),
816 Arc::<Box<dyn FixedRule>>::new(Box::new(RandomWalk)),
817 ),
818 (
819 "ReorderSort".to_string(),
820 Arc::<Box<dyn FixedRule>>::new(Box::new(ReorderSort)),
821 ),
822 (
823 "JsonReader".to_string(),
824 Arc::<Box<dyn FixedRule>>::new(Box::new(JsonReader)),
825 ),
826 (
827 "CsvReader".to_string(),
828 Arc::<Box<dyn FixedRule>>::new(Box::new(CsvReader)),
829 ),
830 (
831 "Constant".to_string(),
832 Arc::<Box<dyn FixedRule>>::new(Box::new(Constant)),
833 ),
834 ])
835 };
836}
837
838impl FixedRuleHandle {
839 pub(crate) fn new(name: &str, span: SourceSpan) -> Self {
840 FixedRuleHandle {
841 name: Symbol::new(name, span),
842 }
843 }
844}
845
846#[derive(Error, Diagnostic, Debug)]
847#[error("The relation cannot be interpreted as an edge")]
848#[diagnostic(code(algo::not_an_edge))]
849#[diagnostic(help("Edge relation requires tuples of length at least two"))]
850struct NotAnEdgeError(#[label] SourceSpan);
851
852#[derive(Error, Diagnostic, Debug)]
853#[error(
854 "The value {0:?} at the third position in the relation cannot be interpreted as edge weights"
855)]
856#[diagnostic(code(algo::invalid_edge_weight))]
857#[diagnostic(help(
858 "Edge weights must be finite numbers. Some algorithm also requires positivity."
859))]
860struct BadEdgeWeightError(DataValue, #[label] SourceSpan);
861
862#[derive(Error, Diagnostic, Debug)]
863#[error("The requested rule '{0}' cannot be found")]
864#[diagnostic(code(algo::rule_not_found))]
865struct RuleNotFoundError(String, #[label] SourceSpan);
866
867#[derive(Error, Diagnostic, Debug)]
868#[error("Invalid reverse scanning of triples")]
869#[diagnostic(code(algo::invalid_reverse_triple_scan))]
870#[diagnostic(help(
871 "Inverse scanning of triples requires the type to be 'ref', or the value be indexed"
872))]
873struct InvalidInverseTripleUse(String, #[label] SourceSpan);
874
875#[derive(Error, Diagnostic, Debug)]
876#[error("Required node with key {missing:?} not found")]
877#[diagnostic(code(algo::node_with_key_not_found))]
878#[diagnostic(help(
879 "The relation is interpreted as a relation of nodes, but the required key is missing"
880))]
881pub(crate) struct NodeNotFoundError {
882 pub(crate) missing: DataValue,
883 #[label]
884 pub(crate) span: SourceSpan,
885}
886
887#[derive(Error, Diagnostic, Debug)]
888#[error("Unacceptable value {0:?} encountered")]
889#[diagnostic(code(algo::unacceptable_value))]
890pub(crate) struct BadExprValueError(
891 pub(crate) DataValue,
892 #[label] pub(crate) SourceSpan,
893 #[help] pub(crate) String,
894);
895
896#[derive(Error, Diagnostic, Debug)]
897#[error("The requested fixed rule '{0}' is not found")]
898#[diagnostic(code(parser::fixed_rule_not_found))]
899pub(crate) struct FixedRuleNotFoundError(pub(crate) String, #[label] pub(crate) SourceSpan);
900
901impl MagicFixedRuleRuleArg {
902 pub(crate) fn arity(
903 &self,
904 tx: &SessionTx<'_>,
905 stores: &BTreeMap<MagicSymbol, EpochStore>,
906 ) -> Result<usize> {
907 Ok(match self {
908 MagicFixedRuleRuleArg::InMem { name, .. } => {
909 let store = stores.get(name).ok_or_else(|| {
910 RuleNotFoundError(name.symbol().to_string(), name.symbol().span)
911 })?;
912 store.arity
913 }
914 MagicFixedRuleRuleArg::Stored { name, .. } => {
915 let handle = tx.get_relation(name, false)?;
916 handle.arity()
917 }
918 })
919 }
920}