use liwe::graph::Graph;
use liwe::model::Key;
use liwe::query::execute;
use liwe::query::{
CountOp, Filter, FindMatch, FindOp, InclusionAnchor, KeyOp, Operation, Outcome, ReferenceAnchor,
};
pub fn all_keys(graph: &Graph) -> Vec<FindMatch> {
match run(&Operation::Find(FindOp::new()), graph) {
Outcome::Find { matches } => matches,
_ => unreachable!("Find returns Find"),
}
}
pub fn key_exists(graph: &Graph, key: &Key) -> bool {
let f = Filter::Key(KeyOp::eq(key.to_string()));
match run(&count_where(f), graph) {
Outcome::Count(n) => n > 0,
_ => unreachable!("Count returns Count"),
}
}
pub fn inclusion_backlinks(graph: &Graph, key: &Key) -> Vec<Key> {
keys_of(run(&find_where(includes_direct(key)), graph))
}
pub fn reference_backlinks(graph: &Graph, key: &Key) -> Vec<Key> {
keys_of(run(&find_where(references_direct(key)), graph))
}
pub fn all_backlinks(graph: &Graph, key: &Key) -> Vec<Key> {
let f = Filter::Or(vec![includes_direct(key), references_direct(key)]);
keys_of(run(&find_where(f), graph))
}
pub fn inclusion_count(graph: &Graph, key: &Key) -> usize {
count_of(run(&count_where(includes_direct(key)), graph))
}
pub fn reference_count(graph: &Graph, key: &Key) -> usize {
count_of(run(&count_where(references_direct(key)), graph))
}
fn run(op: &Operation, graph: &Graph) -> Outcome {
execute(op, graph).expect("find/count queries do not fail")
}
fn find_where(filter: Filter) -> Operation {
Operation::Find(FindOp::new().filter(filter))
}
fn count_where(filter: Filter) -> Operation {
Operation::Count(CountOp::new().filter(filter))
}
fn includes_direct(key: &Key) -> Filter {
Filter::Includes(Box::new(InclusionAnchor::new(key.to_string(), 1, 1)))
}
fn references_direct(key: &Key) -> Filter {
Filter::References(Box::new(ReferenceAnchor::new(key.to_string(), 1, 1)))
}
fn keys_of(outcome: Outcome) -> Vec<Key> {
match outcome {
Outcome::Find { matches } => matches.into_iter().map(|m| m.key).collect(),
_ => unreachable!("Find returns Find"),
}
}
fn count_of(outcome: Outcome) -> usize {
match outcome {
Outcome::Count(n) => n,
_ => unreachable!("Count returns Count"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use itertools::Itertools;
use liwe::graph::{Graph, GraphContext};
use liwe::model::config::MarkdownOptions;
use liwe::model::node::NodePointer;
use liwe::state::from_indoc;
fn make_graph(docs: &str) -> Graph {
Graph::import(&from_indoc(docs), MarkdownOptions::default(), None)
}
fn sorted(mut keys: Vec<Key>) -> Vec<String> {
keys.sort_by(|a, b| a.to_string().cmp(&b.to_string()));
keys.into_iter().map(|k| k.to_string()).collect()
}
fn graph_inclusion_keys(graph: &Graph, key: &Key) -> Vec<String> {
graph
.get_inclusion_edges_to(key)
.into_iter()
.map(|id| graph.node(id).node_key())
.filter(|k| k != key)
.unique()
.map(|k| k.to_string())
.sorted()
.collect()
}
fn graph_reference_keys(graph: &Graph, key: &Key) -> Vec<String> {
graph
.get_reference_edges_to(key)
.into_iter()
.map(|id| graph.node(id).node_key())
.filter(|k| k != key)
.unique()
.map(|k| k.to_string())
.sorted()
.collect()
}
const FIXTURE: &str = indoc! {"
[b](2)
_
[c](3)
_
# C
See [other](2) for details.
"};
#[test]
fn all_keys_matches_graph_keys() {
let graph = make_graph(FIXTURE);
let helper: Vec<String> = all_keys(&graph)
.into_iter()
.map(|m| m.key.to_string())
.sorted()
.collect();
let direct: Vec<String> = graph
.keys()
.into_iter()
.map(|k| k.to_string())
.sorted()
.collect();
assert_eq!(helper, direct);
}
#[test]
fn key_exists_true_for_present_key() {
let graph = make_graph(FIXTURE);
assert!(key_exists(&graph, &Key::name("1")));
assert!(key_exists(&graph, &Key::name("3")));
}
#[test]
fn key_exists_false_for_missing_key() {
let graph = make_graph(FIXTURE);
assert!(!key_exists(&graph, &Key::name("does-not-exist")));
}
#[test]
fn inclusion_backlinks_match_graph_edges() {
let graph = make_graph(FIXTURE);
let target = Key::name("2");
assert_eq!(
sorted(inclusion_backlinks(&graph, &target)),
graph_inclusion_keys(&graph, &target),
);
}
#[test]
fn reference_backlinks_match_graph_edges() {
let graph = make_graph(FIXTURE);
let target = Key::name("2");
assert_eq!(
sorted(reference_backlinks(&graph, &target)),
graph_reference_keys(&graph, &target),
);
}
#[test]
fn all_backlinks_unions_both_kinds() {
let graph = make_graph(FIXTURE);
let target = Key::name("2");
let helper = sorted(all_backlinks(&graph, &target));
let mut combined: Vec<String> = graph_inclusion_keys(&graph, &target)
.into_iter()
.chain(graph_reference_keys(&graph, &target))
.unique()
.collect();
combined.sort();
assert_eq!(helper, combined);
}
#[test]
fn inclusion_count_matches_unique_sources() {
let graph = make_graph(FIXTURE);
let target = Key::name("3");
assert_eq!(
inclusion_count(&graph, &target),
graph_inclusion_keys(&graph, &target).len(),
);
}
#[test]
fn reference_count_matches_unique_sources() {
let graph = make_graph(FIXTURE);
let target = Key::name("2");
assert_eq!(
reference_count(&graph, &target),
graph_reference_keys(&graph, &target).len(),
);
}
}