Skip to main content

mini_kanren/
database.rs

1//! A simple relational database
2
3use crate::core::value::Value;
4use std::collections::HashMap;
5
6pub struct Database {
7    db: HashMap<String, Vec<Vec<Value>>>,
8    default_table: Vec<Vec<Value>>,
9}
10
11impl Database {
12    pub fn new() -> Self {
13        Database {
14            db: HashMap::new(),
15            default_table: vec![],
16        }
17    }
18
19    pub fn insert(&mut self, table_name: &str, row: Vec<Value>) {
20        if let Some(table) = self.db.get_mut(table_name) {
21            table.push(row)
22        } else {
23            self.db.insert(table_name.to_string(), vec![row]);
24        }
25    }
26
27    pub fn query(
28        &self,
29        table_name: &str,
30    ) -> impl Iterator<Item = impl Iterator<Item = &Value> + '_> + '_ {
31        let table = self.db.get(table_name).unwrap_or(&self.default_table);
32        table.into_iter().map(|row| row.into_iter())
33    }
34}
35
36/// Defines a database relation.
37///
38/// The macro produces a goal-generating function, which can be used
39/// to query the database.
40#[macro_export]
41macro_rules! db_rel {
42    ($($rel:ident($($args:ident),*));* $(;)?) => {
43        $(
44            /// Creates a goal that succeeds if the relation is consistent with the database.
45            fn $rel(db: &Arc<$crate::database::Database>, $($args: impl Into<$crate::prelude::Value>),*) -> impl Clone + $crate::prelude::Goal<$crate::prelude::Substitution<'static>> {
46                let db = db.clone();
47                $(let $args = $args.into();)*
48                move |s: $crate::prelude::Substitution<'static>| {
49                    let values = db.query(stringify!($rel));
50                    let subs = values
51                        .filter_map(|mut value| {
52                            let s = s.clone();
53                            $(let s = s.unify(&$args, value.next()?)?;)*
54                            Some(s)
55                        });
56                    $crate::prelude::Stream::from_iter(subs)
57                }
58            }
59        )*
60    };
61}
62
63/// Insert facts into databases.
64#[macro_export]
65macro_rules! db_facts {
66    ($($db:ident { $($rel:ident($($args:expr),*));* $(;)? })*) => {
67        $( $(
68            $db.insert(stringify!($rel), vec![$($crate::prelude::Value::new($args)),*]);
69        )* )*
70    };
71}