use crate::tests::prelude::*;
track_file!("ref/asciidoc-lang/docs/modules/tables/pages/nested.adoc");
fn parse_table(source: &str) -> crate::blocks::TableBlock<'_> {
let mut parser = Parser::default();
let mi = crate::blocks::Block::parse(crate::Span::new(source), &mut parser)
.unwrap_if_no_warnings()
.unwrap();
match mi.item {
crate::blocks::Block::Table(table) => table,
other => panic!("expected a table block, got {other:?}"),
}
}
fn nested_table<'a>(cell: &'a crate::blocks::TableCell<'_>) -> &'a crate::blocks::TableBlock<'a> {
match cell.content() {
crate::blocks::TableCellContent::AsciiDoc(cell) => {
let blocks = cell.blocks();
let tables: Vec<&crate::blocks::TableBlock<'_>> = blocks
.iter()
.filter_map(|block| match block {
crate::blocks::Block::Table(table) => Some(table),
_ => None,
})
.collect();
assert_eq!(tables.len(), 1, "expected exactly one nested table");
tables[0]
}
other => panic!("expected an AsciiDoc cell, got {other:?}"),
}
}
fn simple_text(cell: &crate::blocks::TableCell<'_>) -> String {
match cell.content() {
crate::blocks::TableCellContent::Simple(content) => content.rendered().to_string(),
crate::blocks::TableCellContent::AsciiDoc(_) => panic!("expected simple cell content"),
}
}
const NESTED_EXAMPLE: &str = "[cols=\"1,2a\"]\n|===\n| Col 1 | Col 2\n\n| Cell 1.1\n| Cell 1.2\n\n| Cell 2.1\n| Cell 2.2\n\n[cols=\"2,1\"]\n!===\n! Col1 ! Col2\n\n! C11\n! C12\n\n!===\n\n|===";
non_normative!(
r#"
= Nesting Tables
"#
);
#[test]
fn distinguish_inner_from_outer() {
verifies!(
r#"
Table cells marked with the AsciiDoc table style (`a`) support nested tables in addition to normal block content.
To distinguish the inner table from the enclosing one, you need to use `!===` as the table delimiter and a cell separator that differs from that used for the enclosing table.
"#
);
let outer = parse_table(NESTED_EXAMPLE);
assert_eq!(outer.body_rows().len(), 2);
let last_cell = &outer.body_rows()[1].cells()[1];
assert_eq!(last_cell.style(), ColumnStyle::AsciiDoc);
match last_cell.content() {
crate::blocks::TableCellContent::AsciiDoc(cell) => {
let blocks = cell.blocks();
assert!(
blocks
.iter()
.any(|b| matches!(b, crate::blocks::Block::Table(_))),
"expected a nested table among the cell's blocks"
);
assert!(
blocks
.iter()
.any(|b| !matches!(b, crate::blocks::Block::Table(_))),
"expected normal block content alongside the nested table"
);
}
other => panic!("expected an AsciiDoc cell, got {other:?}"),
}
let inner = nested_table(last_cell);
assert_eq!(inner.header_row().unwrap().cells().len(), 2);
assert_eq!(inner.body_rows().len(), 1);
assert_eq!(inner.body_rows()[0].cells().len(), 2);
assert_eq!(simple_text(&inner.body_rows()[0].cells()[0]), "C11");
assert_eq!(simple_text(&inner.body_rows()[0].cells()[1]), "C12");
let top_level = parse_table("!===\n| a ! b\n!===");
assert_eq!(top_level.body_rows()[0].cells().len(), 1);
assert_eq!(simple_text(&top_level.body_rows()[0].cells()[0]), "a ! b");
let pipe_level = parse_table("|===\n| a ! b\n|===");
assert_eq!(
simple_text(&top_level.body_rows()[0].cells()[0]),
simple_text(&pipe_level.body_rows()[0].cells()[0]),
);
}
#[test]
fn default_separator_and_override() {
verifies!(
r#"
The default cell separator for a nested table is `!`, though you can choose another character by defining the `separator` attribute on the table.
"#
);
let outer = parse_table("|===\na|\n!===\n! one ! two\n!===\n|===");
let inner = nested_table(&outer.body_rows()[0].cells()[0]);
assert_eq!(inner.body_rows()[0].cells().len(), 2);
assert_eq!(simple_text(&inner.body_rows()[0].cells()[0]), "one");
assert_eq!(simple_text(&inner.body_rows()[0].cells()[1]), "two");
let outer = parse_table("|===\na|\n[separator=:]\n!===\n:one :two\n!===\n|===");
let inner = nested_table(&outer.body_rows()[0].cells()[0]);
assert_eq!(inner.body_rows()[0].cells().len(), 2);
assert_eq!(simple_text(&inner.body_rows()[0].cells()[0]), "one");
assert_eq!(simple_text(&inner.body_rows()[0].cells()[1]), "two");
let outer = parse_table("[separator=;]\n|===\n;one ;two\n|===");
assert_eq!(outer.body_rows()[0].cells().len(), 2);
assert_eq!(simple_text(&outer.body_rows()[0].cells()[0]), "one");
assert_eq!(simple_text(&outer.body_rows()[0].cells()[1]), "two");
}
non_normative!(
r#"
NOTE: Although nested tables are not technically valid in DocBook 5.0, the DocBook toolchain processes them anyway.
"#
);
#[test]
fn nested_table_example() {
verifies!(
r#"
The following example contains a nested table in the last cell.
Notice the nested table has its own format, independent of that of the outer table:
[source]
----
include::example$table.adoc[tag=nested]
----
.Result: A nested table
include::example$table.adoc[tag=nested]
"#
);
let outer = parse_table(NESTED_EXAMPLE);
assert_eq!(outer.columns().len(), 2);
assert_eq!(outer.body_rows().len(), 2);
let last_cell = &outer.body_rows()[1].cells()[1];
assert_eq!(last_cell.style(), ColumnStyle::AsciiDoc);
let inner = nested_table(last_cell);
assert_eq!(inner.columns().len(), 2);
assert_eq!(
inner
.header_row()
.unwrap()
.cells()
.iter()
.map(simple_text)
.collect::<Vec<_>>(),
vec!["Col1", "Col2"]
);
assert_eq!(inner.body_rows().len(), 1);
assert_eq!(
inner.body_rows()[0]
.cells()
.iter()
.map(simple_text)
.collect::<Vec<_>>(),
vec!["C11", "C12"]
);
}
non_normative!(
r#"
We recommend using nested tables sparingly.
There's usually a better way to present the information.
"#
);