use quarb_text::{Block, Container, TextModel};
fn values(model: &TextModel, query: &str) -> Vec<String> {
match quarb::run(query, model).unwrap() {
quarb::QueryResult::Values(vs) => vs.iter().map(|v| v.to_string()).collect(),
quarb::QueryResult::Nodes(_) => panic!("expected values"),
}
}
fn nodes(model: &TextModel, query: &str) -> Vec<String> {
let mut got: Vec<String> = match quarb::run(query, model).unwrap() {
quarb::QueryResult::Nodes(ns) => ns.into_iter().map(|n| model.locator(n)).collect(),
quarb::QueryResult::Values(_) => panic!("expected nodes"),
};
got.sort();
got
}
fn outline() -> TextModel {
TextModel::build(vec![
Block::Paragraph {
text: "Preamble.".into(),
},
Block::Heading {
level: 1,
lemma: "One".into(),
},
Block::Paragraph {
text: "In one.".into(),
},
Block::Heading {
level: 3,
lemma: "Deep".into(),
},
Block::Paragraph {
text: "In deep.".into(),
},
Block::Heading {
level: 2,
lemma: "Mid".into(),
},
Block::Paragraph {
text: "In mid.".into(),
},
Block::Heading {
level: 1,
lemma: "Two".into(),
},
Block::Paragraph {
text: "In two.".into(),
},
])
}
#[test]
fn sections_derive_from_flat_headings() {
let m = outline();
assert_eq!(values(&m, "/section::lemma"), vec!["One", "Two"]);
assert_eq!(
nodes(&m, "/section/section"),
vec!["/section[1]/section[1]", "/section[1]/section[2]"]
);
assert_eq!(values(&m, "/section/section::lemma"), vec!["Deep", "Mid"]);
assert_eq!(values(&m, "/section/section::::level"), vec!["3", "2"]);
assert_eq!(values(&m, "/paragraph::"), vec!["Preamble."]);
}
#[test]
fn prose_flattens_with_lemma() {
let m = outline();
assert_eq!(
values(&m, "/section[::lemma = 'Two']::"),
vec!["Two\nIn two."]
);
}
#[test]
fn heading_inside_container_lowers_to_paragraph() {
let m = TextModel::build(vec![
Block::Open {
kind: Container::Blockquote,
lemma: None,
},
Block::Heading {
level: 2,
lemma: "Decorative".into(),
},
Block::Paragraph {
text: "Quoted.".into(),
},
Block::Close { hypograph: None },
]);
assert_eq!(nodes(&m, "//section"), Vec::<String>::new());
assert_eq!(
values(&m, "/blockquote/paragraph::"),
vec!["Decorative", "Quoted."]
);
}
#[test]
fn blockquote_hypograph() {
let m = TextModel::build(vec![
Block::Open {
kind: Container::Blockquote,
lemma: None,
},
Block::Paragraph {
text: "Quoted wisdom.".into(),
},
Block::Close {
hypograph: Some("— Sage".into()),
},
]);
assert_eq!(values(&m, "/blockquote::hypograph"), vec!["— Sage"]);
assert_eq!(values(&m, "/blockquote::"), vec!["Quoted wisdom.\n— Sage"]);
}
#[test]
fn lists_and_items() {
let m = TextModel::build(vec![
Block::Open {
kind: Container::UnorderedList,
lemma: None,
},
Block::Open {
kind: Container::Item,
lemma: None,
},
Block::Text {
text: "alpha".into(),
},
Block::Close { hypograph: None },
Block::Open {
kind: Container::Item,
lemma: None,
},
Block::Text {
text: "beta".into(),
},
Block::Open {
kind: Container::UnorderedList,
lemma: None,
},
Block::Open {
kind: Container::Item,
lemma: None,
},
Block::Text {
text: "beta-child".into(),
},
Block::Close { hypograph: None },
Block::Close { hypograph: None },
Block::Close { hypograph: None },
Block::Close { hypograph: None },
Block::Open {
kind: Container::OrderedList { start: 3 },
lemma: None,
},
Block::Open {
kind: Container::Item,
lemma: None,
},
Block::Text {
text: "third".into(),
},
Block::Close { hypograph: None },
Block::Open {
kind: Container::Item,
lemma: None,
},
Block::Text {
text: "fourth".into(),
},
Block::Close { hypograph: None },
Block::Close { hypograph: None },
]);
assert_eq!(
values(&m, "/unordered-list/unordered-item::"),
vec!["alpha", "beta\nbeta-child"]
);
assert_eq!(
nodes(&m, "//unordered-item/unordered-list/unordered-item"),
vec!["/unordered-list/unordered-item[2]/unordered-list/unordered-item"]
);
assert_eq!(values(&m, "//ordered-item::taxis"), vec!["3", "4"]);
}
#[test]
fn tables_denormalize_to_nested_lists() {
let m = TextModel::build(vec![Block::Table {
lemma: Some("Crew".into()),
headers: Some(vec!["Name".into(), "Role".into()]),
rows: vec![
vec!["Alice".into(), "captain".into()],
vec!["Bob".into(), "".into()],
],
}]);
assert_eq!(nodes(&m, "//*<table>"), vec!["/ordered-list"]);
assert_eq!(values(&m, "/ordered-list::lemma"), vec!["Crew"]);
assert_eq!(values(&m, "//ordered-item::taxis"), vec!["1", "2"]);
assert_eq!(
values(&m, "//unordered-item::"),
vec!["Name: Alice", "Role: captain", "Name: Bob"]
);
}
#[test]
fn headerless_table_keeps_bare_cells() {
let m = TextModel::build(vec![Block::Table {
lemma: None,
headers: None,
rows: vec![vec!["Alice".into(), "captain".into()]],
}]);
assert_eq!(
values(&m, "//unordered-item::"),
vec!["Alice", "captain"]
);
}
#[test]
fn plain_text_paragraphs() {
let m = TextModel::parse_plain("One line\nsame paragraph.\n\n \nSecond paragraph.\n");
assert_eq!(
values(&m, "/paragraph::"),
vec!["One line same paragraph.", "Second paragraph."]
);
assert_eq!(nodes(&m, "//section"), Vec::<String>::new());
}