Skip to main content

datafox/
lib.rs

1#![forbid(unsafe_code)]
2//! Datafox is a standalone Datalog parser and streaming query engine for facts.
3//!
4//! The crate is intentionally small: callers provide facts through [`Storage`],
5//! parse read-only queries with [`parse_query`] or [`parse_queries`], and evaluate
6//! them with a [`DatafoxClient`] configured for the runtime profile you need.
7//!
8//! ```
9//! use datafox::{DatafoxClient, DatafoxConfig, InMemoryStorage, Value, parse_query};
10//!
11//! let storage = InMemoryStorage::from_facts([(
12//!     "edge".to_string(),
13//!     vec![
14//!         vec![Value::integer(1), Value::integer(2)],
15//!         vec![Value::integer(2), Value::integer(3)],
16//!     ],
17//! )]);
18//! let query = parse_query("edge(From, 2)")?;
19//! let datafox = DatafoxClient::new(DatafoxConfig::new(&storage))?;
20//! let results = datafox.eval(&query)?.collect::<Vec<_>>();
21//!
22//! assert_eq!(results.len(), 1);
23//! assert_eq!(results[0].lookup("From"), Some(&Value::integer(1)));
24//! # Ok::<(), datafox::Error>(())
25//! ```
26//!
27//! Public API:
28//! - [`Value`] for Datalog constants.
29//! - [`Term`] for variables, constants, and wildcards.
30//! - [`Atom`], [`Clause`], and [`Query`] for query syntax trees.
31//! - [`Diagnostic`] and [`parse_query`] for query parsing with context.
32//! - [`format_query`] and [`format_queries`] for stable, readable query files.
33//! - [`Substitution`] and [`Unifier`] for binding and matching query variables.
34//! - [`DatafoxClient`], [`DatafoxConfig`], [`DatafoxEnvironment`],
35//!   [`PreparedQuery`], [`PreparedQueryStorage`], [`Planner`], [`Plan`], and [`Storage`]
36//!   for snapshot-based query execution.
37//! - [`Prelude`], [`BinaryRelation`], and [`BinaryOperator`] for ambient facts,
38//!   builtin relations, and expression operators.
39//! - [`Error`] and [`Result`] for typed failures.
40//! - [`atom!`], [`var!`], [`lit!`], and [`subst!`] for test and call-site ergonomics.
41
42mod ast;
43mod client;
44mod diagnostic;
45pub mod error;
46mod evaluator;
47mod parser;
48mod plan;
49mod prelude;
50mod pretty;
51mod storage;
52mod substitution;
53mod term;
54mod unify;
55mod universe;
56mod value;
57
58pub use ast::{Atom, Clause, Query};
59pub use client::{
60    DatafoxClient, DatafoxConfig, DatafoxEnvironment, DatafoxEnvironmentBuilder,
61    InMemoryPreparedQueryStorage, PlanningCache, PreparedQueryKey, PreparedQueryStorage,
62};
63pub use diagnostic::{Diagnostic, Span};
64pub use error::{Error, Result};
65pub use evaluator::{Evaluation, EvaluationStrategy};
66pub use parser::{parse_queries, parse_query};
67pub use plan::{PREPARED_QUERY_FORMAT_VERSION, Plan, Planner, PreparedQuery};
68pub use prelude::{BinaryOperator, BinaryRelation, OperatorOutcome, Prelude, RelationOutcome};
69pub use pretty::{format_queries, format_query};
70pub use storage::{
71    AtomRole, FactEstimate, FactRequest, FactRequestHints, FactRequestMode, FactScan, FactStore,
72    FactTuple, InMemoryStorage, PatternValue, Projection, SnapshotSelector, Storage, TupleStream,
73    matches_pattern,
74};
75pub use substitution::Substitution;
76pub use term::Term;
77pub use unify::Unifier;
78pub use universe::Universe;
79pub use value::Value;
80
81#[macro_export]
82macro_rules! atom {
83    ($name:expr, $args:expr) => {{ $crate::Atom::new($name, $args).expect("invalid atom") }};
84}
85
86#[macro_export]
87macro_rules! var {
88    ($name:expr) => {{ $crate::Term::variable($name).expect("invalid variable") }};
89}
90
91#[macro_export]
92macro_rules! lit {
93    ($value:expr) => {{ $crate::Term::constant($value) }};
94}
95
96#[macro_export]
97macro_rules! subst {
98    ($(($name:expr, $value:expr)),* $(,)?) => {{
99        $crate::Substitution::from_bindings(vec![
100            $(($name.to_string(), $value)),*
101        ])
102    }};
103}
104
105#[cfg(test)]
106mod tests {
107    use crate::{Atom, Substitution, Term, Value};
108
109    #[test]
110    fn convenience_macros_build_terms_atoms_and_substitutions() {
111        let atom = atom!(
112            "spotify:displayName",
113            vec![crate::var!("Album"), crate::lit!(Value::string("2112"))]
114        );
115        let substitution = crate::subst![
116            ("Album", Value::string("spotify:album:2112")),
117            ("Name", Value::string("2112")),
118        ];
119
120        assert_eq!(
121            atom,
122            Atom::new(
123                "spotify:displayName",
124                vec![
125                    Term::variable("Album").expect("variable"),
126                    Term::constant(Value::string("2112")),
127                ],
128            )
129            .expect("atom"),
130        );
131        assert_eq!(
132            substitution,
133            Substitution::from_bindings(vec![
134                ("Album".to_string(), Value::string("spotify:album:2112")),
135                ("Name".to_string(), Value::string("2112")),
136            ]),
137        );
138    }
139}