extern crate mentat;
use mentat::{Queryable, Store};
fn setup() -> Store {
let mut s = Store::open("").expect("opened");
s.transact(
r#"[
{:db/ident :t/kind :db/valueType :db.type/keyword :db/cardinality :db.cardinality/one :db/index true}
{:db/ident :t/value :db/valueType :db.type/string :db/cardinality :db.cardinality/one}
{:db/ident :t/private :db/valueType :db.type/boolean :db/cardinality :db.cardinality/one}
{:db/ident :t/rank :db/valueType :db.type/long :db/cardinality :db.cardinality/one}
{:db/ident :t/tag :db/valueType :db.type/string :db/cardinality :db.cardinality/many}
{:db/ident :t/person :db/valueType :db.type/ref :db/cardinality :db.cardinality/one}
{:db/ident :p/email :db/valueType :db.type/string :db/cardinality :db.cardinality/one
:db/unique :db.unique/identity :db/index true}
]"#,
)
.expect("schema");
s.transact(
r#"[{:db/id "x" :p/email "x@example.com"}
{:t/kind :k/chat :t/value "a" :t/person "x" :t/rank 3}
{:t/kind :k/chat :t/value "b" :t/person "x" :t/private true}
{:t/kind :k/mail :t/value "c" :t/private false}]"#,
)
.expect("data");
s
}
fn rows(s: &Store, q: &str) -> Vec<String> {
let out = s.q_once(q, None).unwrap_or_else(|e| panic!("{q}: {e:?}"));
let mut v: Vec<String> = out
.results
.into_rel()
.expect("rel")
.into_iter()
.map(|row| {
let cells: Vec<String> = row
.iter()
.map(|b| match b.clone().into_scalar() {
Some(v) => format!("{v:?}"),
None => format!("{b:?}"),
})
.collect();
format!("[{}]", cells.join(", "))
})
.collect();
v.sort();
v
}
fn expect(s: &Store, q: &str, want: &[&str]) {
assert_eq!(
rows(s, q),
want.iter().map(|w| w.to_string()).collect::<Vec<_>>(),
"{q}"
);
}
#[test]
fn get_else_one_and_several_patterns() {
let s = setup();
expect(
&s,
"[:find ?v ?p :where [?c :t/value ?v] [(get-else $ ?c :t/private false) ?p]]",
&[
r#"[String("a"), Boolean(false)]"#,
r#"[String("b"), Boolean(true)]"#,
r#"[String("c"), Boolean(false)]"#,
],
);
expect(
&s,
"[:find ?v ?p :where [?c :t/kind :k/chat] [?c :t/value ?v] \
[(get-else $ ?c :t/private false) ?p]]",
&[
r#"[String("a"), Boolean(false)]"#,
r#"[String("b"), Boolean(true)]"#,
],
);
expect(
&s,
"[:find ?k ?v ?priv :where [?p :p/email \"x@example.com\"] [?c :t/person ?p] \
[?c :t/kind ?k] [?c :t/value ?v] [(get-else $ ?c :t/private false) ?priv]]",
&[
r#"[Keyword(Keyword(NamespaceableName { namespace: Some("k"), name: "chat" })), String("a"), Boolean(false)]"#,
r#"[Keyword(Keyword(NamespaceableName { namespace: Some("k"), name: "chat" })), String("b"), Boolean(true)]"#,
],
);
}
#[test]
fn get_else_twice_and_on_a_ref_value() {
let s = setup();
expect(
&s,
"[:find ?v ?priv ?rank ?mail :where [?c :t/kind :k/chat] [?c :t/value ?v] [?c :t/person ?p] \
[(get-else $ ?c :t/private false) ?priv] [(get-else $ ?c :t/rank 0) ?rank] \
[(get-else $ ?p :p/email \"none\") ?mail]]",
&[
r#"[String("a"), Boolean(false), Long(3), String("x@example.com")]"#,
r#"[String("b"), Boolean(true), Long(0), String("x@example.com")]"#,
],
);
}
#[test]
fn predicate_on_a_get_else_result() {
let s = setup();
expect(
&s,
"[:find ?v :where [?c :t/kind :k/chat] [?c :t/value ?v] \
[(get-else $ ?c :t/rank 0) ?r] [(> ?r 1)]]",
&[r#"[String("a")]"#],
);
}
#[test]
fn get_else_on_cardinality_many_is_an_error() {
let s = setup();
let err = s
.q_once(
"[:find ?v ?t :where [?c :t/value ?v] [(get-else $ ?c :t/tag \"none\") ?t]]",
None,
)
.expect_err("card-many get-else must be rejected");
assert!(format!("{err:?}").contains("cardinality-many"), "{err:?}");
}
#[test]
fn missing_alone_and_with_get_else() {
let s = setup();
expect(
&s,
"[:find ?v :where [?c :t/value ?v] [(missing? $ ?c :t/private)]]",
&[r#"[String("a")]"#],
);
expect(
&s,
"[:find ?v ?r :where [?c :t/kind :k/chat] [?c :t/value ?v] \
[(missing? $ ?c :t/private)] [(get-else $ ?c :t/rank 0) ?r]]",
&[r#"[String("a"), Long(3)]"#],
);
}