use quarb::{QueryResult, Value};
fn doc() -> quarb_json::JsonAdapter {
quarb_json::JsonAdapter::parse(
r#"{"rows":[
{"name":"Iliad","pages":683,"year":-750},
{"name":"Emma","pages":474,"year":1815},
{"name":"Hamlet","pages":342,"year":1603},
{"name":"Emma","pages":474,"year":1815}]}"#,
)
.unwrap()
}
fn run(q: &str) -> quarb::Result<Vec<String>> {
let run = quarb::Run::ambient();
let out = quarb::Query::parse(q, &doc())?.run(&doc(), &run)?;
Ok(match out.result {
QueryResult::Values(vs) => vs.iter().map(Value::display_form).collect(),
QueryResult::Nodes(ns) => ns.iter().map(|n| format!("{n:?}")).collect(),
})
}
fn one(q: &str) -> String {
let v = run(q).unwrap();
assert_eq!(v.len(), 1, "{q}: {v:?}");
v.into_iter().next().unwrap()
}
const TABLE: &str = "/rows/*[1..3] | %(::name; ::pages; ::year) @| #";
const DUPES: &str = "/rows/* | %(::name; ::pages; ::year) @| #";
const ILIAD: &str = "%(\"Iliad\"; 683; -750)";
const EMMA: &str = "%(\"Emma\"; 474; 1815)";
const HAMLET: &str = "%(\"Hamlet\"; 342; 1603)";
fn table(rows: &[&str]) -> String {
format!("#(@(name; pages; year);\n {})", rows.join(";\n "))
}
#[test]
fn a_keyed_aggregate_ranks_the_rows_of_one_table() {
assert_eq!(one(TABLE), table(&[ILIAD, EMMA, HAMLET]));
assert_eq!(
one(&format!("{TABLE} @| sort_by(:pages)")),
table(&[HAMLET, EMMA, ILIAD])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(:pages; --desc)")),
table(&[ILIAD, EMMA, HAMLET])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(:name; -d)")),
table(&[ILIAD, HAMLET, EMMA])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(:name; - :pages; --desc)")),
table(&[ILIAD, HAMLET, EMMA])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(:pages; --desc) @| sort_by(:name)")),
table(&[EMMA, HAMLET, ILIAD])
);
assert_eq!(
one(&format!("{TABLE} @| top(2; :pages)")),
table(&[ILIAD, EMMA])
);
assert_eq!(
one(&format!("{TABLE} @| bottom(1; :year)")),
table(&[ILIAD])
);
assert_eq!(one(&format!("{TABLE} @| max_by(:year)")), table(&[EMMA]));
assert_eq!(
one(&format!("{DUPES} @| unique_by(:name)")),
table(&[ILIAD, EMMA, HAMLET])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(:pages; --desc) | [:year > 0]")),
table(&[EMMA, HAMLET])
);
}
#[test]
fn the_ordering_reductions_read_the_rows() {
assert_eq!(
one(&format!("{TABLE} | reverse")),
table(&[HAMLET, EMMA, ILIAD])
);
assert_eq!(
one(&format!("{TABLE} @| reverse")),
table(&[HAMLET, EMMA, ILIAD])
);
assert_eq!(
one(&format!("{TABLE} | first")),
"%(name = \"Iliad\"; pages = 683; year = -750)"
);
assert_eq!(
one(&format!("{TABLE} | last")),
"%(name = \"Hamlet\"; pages = 342; year = 1603)"
);
assert_eq!(one(&format!("{TABLE} | first(2)")), table(&[ILIAD, EMMA]));
assert_eq!(
one(&format!("{TABLE} | last(1..2)")),
table(&[EMMA, HAMLET])
);
assert_eq!(
one(&format!("{DUPES} | unique")),
table(&[ILIAD, EMMA, HAMLET])
);
for q in [format!("{TABLE} | sort"), format!("{TABLE} @| sort")] {
let e = run(&q).unwrap_err().to_string();
assert!(e.contains("sorts by a column") && e.contains("sort_by(:col; --desc)"), "{q}: {e}");
}
}
#[test]
fn desc_on_values_and_the_option_checks() {
assert_eq!(
run("/rows/*[1..3] | ::pages @| sort(--desc)").unwrap(),
vec!["683", "474", "342"]
);
assert_eq!(
run("/rows/*[1..3] | ::pages @| sort(-d)").unwrap(),
vec!["683", "474", "342"]
);
assert_eq!(
run("/rows/*[1..3] | ::name @| sort(atergo; --desc)").unwrap(),
vec!["Hamlet", "Iliad", "Emma"]
);
assert_eq!(
run("/rows/*[1..3] @| sort_by(::pages; --desc) | ::name").unwrap(),
vec!["Iliad", "Emma", "Hamlet"]
);
assert_eq!(
one("/rows/*[1..3] | ::pages @| @ | sort(--desc)"),
"@(683; 474; 342)"
);
let e = run("/rows/* @| sort_by(--desc)").unwrap_err().to_string();
assert!(e.contains("needs value-expression keys"), "{e}");
let e = run("/rows/* @| sort_by(::pages; --nope)").unwrap_err().to_string();
assert!(e.contains("--desc"), "{e}");
}
#[test]
fn the_glued_minus_descends_on_one_key_of_any_type() {
assert_eq!(
run("/rows/*[1..3] @| sort_by(-::name) | ::name").unwrap(),
vec!["Iliad", "Hamlet", "Emma"]
);
assert_eq!(
run("/rows/*[1..3] @| sort_by(-::pages) | ::name").unwrap(),
vec!["Iliad", "Emma", "Hamlet"]
);
assert_eq!(
run("/rows/*[1..3] @| sort_by(- ::pages) | ::name").unwrap(),
vec!["Iliad", "Emma", "Hamlet"]
);
assert_eq!(
run("/rows/*[1..3] @| sort_by(-::name; --desc) | ::name").unwrap(),
vec!["Emma", "Hamlet", "Iliad"]
);
assert_eq!(
run("/rows/* @| sort_by(-::name; ::year) | ::year").unwrap(),
vec!["-750", "1603", "1815", "1815"]
);
assert_eq!(
run("/rows/* @| sort_by(::year) @| sort_by(::name; --desc) | ::year").unwrap(),
run("/rows/* @| sort_by(-::name; ::year) | ::year").unwrap()
);
assert_eq!(
run("/rows/*[1..3] @| top(2; -::pages) | ::name").unwrap(),
vec!["Hamlet", "Emma"]
);
assert_eq!(run("/rows/*[1] | %(n = -::pages)").unwrap(), vec!["%(n = -683)"]);
let e = run("/rows/*[1] | ::pages -::year").unwrap_err().to_string();
assert!(e.contains("spaced on both sides"), "{e}");
}
#[test]
fn the_rounded_minus_in_keyed_parentheses_is_the_descending_core_field() {
assert_eq!(
one(&format!("{TABLE} @| sort_by(-:pages)")),
table(&[ILIAD, EMMA, HAMLET])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(-:name; :pages)")),
table(&[ILIAD, HAMLET, EMMA])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(-:name; --desc)")),
table(&[EMMA, HAMLET, ILIAD])
);
assert_eq!(
one(&format!("{TABLE} @| top(2; -:pages)")),
table(&[HAMLET, EMMA])
);
assert_eq!(
one(&format!("{TABLE} @| sort_by(<-author)")),
table(&[ILIAD, EMMA, HAMLET])
);
assert!(run("/rows/*-:author | ::name").unwrap().is_empty());
}