Merge branch 'test_improvement'
This commit is contained in:
commit
31da2e970c
6
Makefile
6
Makefile
@ -24,12 +24,16 @@ clean:
|
|||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
> cargo test --lib
|
> cargo test --lib --test test_loader
|
||||||
|
|
||||||
.PHONY: integrationtest
|
.PHONY: integrationtest
|
||||||
integrationtest:
|
integrationtest:
|
||||||
> cargo test --no-fail-fast --test test_loader
|
> cargo test --no-fail-fast --test test_loader
|
||||||
|
|
||||||
|
.PHONY: unittest
|
||||||
|
unittest:
|
||||||
|
> cargo test --lib
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run:
|
run:
|
||||||
> cargo run
|
> cargo run
|
||||||
|
33
build.rs
33
build.rs
@ -22,17 +22,28 @@ fn main() {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
Err(_) => true,
|
Err(_) => true,
|
||||||
}).collect::<Result<Vec<_>, _>>().unwrap();
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.unwrap();
|
||||||
for test in test_files {
|
for test in test_files {
|
||||||
write_test(&mut test_file, &test);
|
write_test(&mut test_file, &test);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
||||||
let test_name = test.path().strip_prefix("org_mode_samples/").expect("Paths should be under org_mode_samples/").to_string_lossy().to_lowercase().strip_suffix(".org").expect("Should have .org extension").replace("/", "_");
|
let test_name = test
|
||||||
|
.path()
|
||||||
|
.strip_prefix("org_mode_samples/")
|
||||||
|
.expect("Paths should be under org_mode_samples/")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_lowercase()
|
||||||
|
.strip_suffix(".org")
|
||||||
|
.expect("Should have .org extension")
|
||||||
|
.replace("/", "_");
|
||||||
|
|
||||||
|
if let Some(reason) = is_expect_fail(test_name.as_str()) {
|
||||||
|
write!(test_file, "#[ignore]\n").unwrap();
|
||||||
|
}
|
||||||
write!(
|
write!(
|
||||||
test_file,
|
test_file,
|
||||||
include_str!("./tests/test_template"),
|
include_str!("./tests/test_template"),
|
||||||
@ -56,3 +67,17 @@ use organic::sexp;
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_expect_fail(name: &str) -> Option<&str> {
|
||||||
|
match name {
|
||||||
|
"drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
||||||
|
"element_container_priority_drawer_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||||
|
"element_container_priority_dynamic_block_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||||
|
"element_container_priority_footnote_definition_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||||
|
"element_container_priority_greater_block_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||||
|
"element_container_priority_section_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||||
|
"exit_matcher_investigation_table_list" => Some("Table needs to be implemented."),
|
||||||
|
"element_container_priority_readme" => Some("Table needs to be implemented."),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
27
scripts/run_integration_test.bash
Executable file
27
scripts/run_integration_test.bash
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
cd "$DIR/../"
|
||||||
|
|
||||||
|
samples_dir=$(readlink -f "org_mode_samples")
|
||||||
|
|
||||||
|
function get_test_names {
|
||||||
|
for test_file in "$@"
|
||||||
|
do
|
||||||
|
if [ -e "$test_file" ]; then
|
||||||
|
test_file_full_path=$(readlink -f "$test_file")
|
||||||
|
relative_to_samples=$(realpath --relative-to "$samples_dir" "$test_file_full_path")
|
||||||
|
without_extension="${relative_to_samples%.org}"
|
||||||
|
echo "${without_extension/\//_}"
|
||||||
|
else
|
||||||
|
echo "$test_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
get_test_names "$@" | while read test; do
|
||||||
|
cargo test --no-fail-fast --test test_loader "$test"
|
||||||
|
done
|
@ -1,4 +1,6 @@
|
|||||||
use super::sexp::Token;
|
use super::sexp::Token;
|
||||||
|
use super::util::assert_bounds;
|
||||||
|
use super::util::assert_name;
|
||||||
use crate::compare::util::get_offsets;
|
use crate::compare::util::get_offsets;
|
||||||
use crate::parser::Comment;
|
use crate::parser::Comment;
|
||||||
use crate::parser::Document;
|
use crate::parser::Document;
|
||||||
@ -18,6 +20,7 @@ use crate::DynamicBlock;
|
|||||||
pub struct DiffResult {
|
pub struct DiffResult {
|
||||||
status: DiffStatus,
|
status: DiffStatus,
|
||||||
name: String,
|
name: String,
|
||||||
|
message: Option<String>,
|
||||||
children: Vec<DiffResult>,
|
children: Vec<DiffResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,13 +74,11 @@ pub fn compare_document<'s>(
|
|||||||
rust: &'s Document<'s>,
|
rust: &'s Document<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
|
||||||
let first_child_text = first_child.as_atom()?;
|
|
||||||
if first_child_text != "org-data" {
|
|
||||||
return Err("Document should correspond to an org-data cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "org-data").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
// Skipping "org-data" and the first parameter which is often nil
|
// Skipping "org-data" and the first parameter which is often nil
|
||||||
for (i, token) in children.iter().skip(2).enumerate() {
|
for (i, token) in children.iter().skip(2).enumerate() {
|
||||||
@ -112,6 +113,7 @@ pub fn compare_document<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "document".to_owned(),
|
name: "document".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -122,29 +124,13 @@ fn compare_section<'s>(
|
|||||||
rust: &'s Section<'s>,
|
rust: &'s Section<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
|
||||||
let first_child_text = first_child.as_atom()?;
|
|
||||||
if first_child_text != "section" {
|
|
||||||
return Err("Section should correspond to a section cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let mut child_status = Vec::new();
|
||||||
|
if assert_name(emacs, "section").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +141,7 @@ fn compare_section<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "section".to_owned(),
|
name: "section".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -165,29 +152,13 @@ fn compare_heading<'s>(
|
|||||||
rust: &'s Heading<'s>,
|
rust: &'s Heading<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
|
||||||
let first_child_text = first_child.as_atom()?;
|
|
||||||
if first_child_text != "headline" {
|
|
||||||
return Err("Heading should correspond to a headline cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "headline").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +176,7 @@ fn compare_heading<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "heading".to_owned(),
|
name: "heading".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -231,31 +203,13 @@ fn compare_paragraph<'s>(
|
|||||||
rust: &'s Paragraph<'s>,
|
rust: &'s Paragraph<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "paragraph" {
|
|
||||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "paragraph").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +218,7 @@ fn compare_paragraph<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "paragraph".to_owned(),
|
name: "paragraph".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -274,31 +229,13 @@ fn compare_plain_list<'s>(
|
|||||||
rust: &'s PlainList<'s>,
|
rust: &'s PlainList<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "plain-list" {
|
|
||||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "plain-list").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +246,7 @@ fn compare_plain_list<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "plain-list".to_owned(),
|
name: "plain-list".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -319,31 +257,13 @@ fn compare_plain_list_item<'s>(
|
|||||||
rust: &'s PlainListItem<'s>,
|
rust: &'s PlainListItem<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "item" {
|
|
||||||
return Err("PlainListItem should correspond to an item cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "item").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,6 +274,7 @@ fn compare_plain_list_item<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "plain-list-item".to_owned(),
|
name: "plain-list-item".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -364,43 +285,20 @@ fn compare_greater_block<'s>(
|
|||||||
rust: &'s GreaterBlock<'s>,
|
rust: &'s GreaterBlock<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
match rust.name.to_lowercase().as_str() {
|
|
||||||
"center" => {
|
|
||||||
if first_child != "center-block" {
|
|
||||||
return Err(
|
|
||||||
"Center greater blocks should correspond to a center-block cell.".into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"quote" => {
|
|
||||||
if first_child != "quote-block" {
|
|
||||||
return Err("Quote greater blocks should correspond to a quote-block cell.".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(
|
||||||
|
emacs,
|
||||||
|
match rust.name.to_lowercase().as_str() {
|
||||||
|
"center" => "center-block",
|
||||||
|
"quote" => "quote-block",
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
).is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,6 +309,7 @@ fn compare_greater_block<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "greater-block".to_owned(),
|
name: "greater-block".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -421,31 +320,13 @@ fn compare_dynamic_block<'s>(
|
|||||||
rust: &'s DynamicBlock<'s>,
|
rust: &'s DynamicBlock<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "dynamic-block" {
|
|
||||||
return Err("Dynamic block should correspond to a dynamic-block cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "dynamic-block").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +337,7 @@ fn compare_dynamic_block<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "dynamic-block".to_owned(),
|
name: "dynamic-block".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -466,31 +348,13 @@ fn compare_footnote_definition<'s>(
|
|||||||
rust: &'s FootnoteDefinition<'s>,
|
rust: &'s FootnoteDefinition<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "footnote-definition" {
|
|
||||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "footnote-definition").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +365,7 @@ fn compare_footnote_definition<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "footnote-definition".to_owned(),
|
name: "footnote-definition".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -511,37 +376,20 @@ fn compare_comment<'s>(
|
|||||||
rust: &'s Comment<'s>,
|
rust: &'s Comment<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "comment" {
|
|
||||||
return Err("Comment should correspond to a comment cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "comment").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "comment".to_owned(),
|
name: "comment".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -552,31 +400,13 @@ fn compare_drawer<'s>(
|
|||||||
rust: &'s Drawer<'s>,
|
rust: &'s Drawer<'s>,
|
||||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let first_child = children
|
|
||||||
.first()
|
|
||||||
.ok_or("Should have at least one child.")?
|
|
||||||
.as_atom()?;
|
|
||||||
if first_child != "drawer" {
|
|
||||||
return Err("Drawer should correspond to a drawer cell.".into());
|
|
||||||
}
|
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
if assert_name(emacs, "drawer").is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
let attributes_child = children
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let begin = attributes_map
|
|
||||||
.get(":begin")
|
|
||||||
.ok_or("Missing :begin attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let end = attributes_map
|
|
||||||
.get(":end")
|
|
||||||
.ok_or("Missing :end attribute.")?
|
|
||||||
.as_atom()?;
|
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
|
||||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,6 +417,7 @@ fn compare_drawer<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: "drawer".to_owned(),
|
name: "drawer".to_owned(),
|
||||||
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::parser::Source;
|
use crate::parser::Source;
|
||||||
|
|
||||||
|
use super::sexp::Token;
|
||||||
|
|
||||||
/// Check if the child string slice is a slice of the parent string slice.
|
/// Check if the child string slice is a slice of the parent string slice.
|
||||||
fn is_slice_of(parent: &str, child: &str) -> bool {
|
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||||
let parent_start = parent.as_ptr() as usize;
|
let parent_start = parent.as_ptr() as usize;
|
||||||
@ -19,3 +21,46 @@ pub fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (u
|
|||||||
let end = offset + rust_object_source.len();
|
let end = offset + rust_object_source.len();
|
||||||
(offset, end)
|
(offset, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assert_name<'s>(emacs: &'s Token<'s>, name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let children = emacs.as_list()?;
|
||||||
|
let first_child = children
|
||||||
|
.first()
|
||||||
|
.ok_or("Should have at least one child.")?
|
||||||
|
.as_atom()?;
|
||||||
|
if first_child != name {
|
||||||
|
Err(format!(
|
||||||
|
"Expected a {expected} cell, but found a {found} cell.",
|
||||||
|
expected = name,
|
||||||
|
found = first_child
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assert_bounds<'s, S: Source<'s>>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &'s Token<'s>,
|
||||||
|
rust: &'s S,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let children = emacs.as_list()?;
|
||||||
|
let attributes_child = children
|
||||||
|
.iter()
|
||||||
|
.nth(1)
|
||||||
|
.ok_or("Should have an attributes child.")?;
|
||||||
|
let attributes_map = attributes_child.as_map()?;
|
||||||
|
let begin = attributes_map
|
||||||
|
.get(":begin")
|
||||||
|
.ok_or("Missing :begin attribute.")?
|
||||||
|
.as_atom()?;
|
||||||
|
let end = attributes_map
|
||||||
|
.get(":end")
|
||||||
|
.ok_or("Missing :end attribute.")?
|
||||||
|
.as_atom()?;
|
||||||
|
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||||
|
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||||
|
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
|
|||||||
use nom::bytes::complete::take_while;
|
use nom::bytes::complete::take_while;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
@ -17,6 +18,7 @@ use crate::parser::exiting::ExitClass;
|
|||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
use crate::parser::util::blank_line;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::immediate_in_section;
|
use crate::parser::util::immediate_in_section;
|
||||||
@ -24,6 +26,8 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
|||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||||
use crate::parser::Drawer;
|
use crate::parser::Drawer;
|
||||||
|
use crate::parser::Element;
|
||||||
|
use crate::parser::Paragraph;
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> {
|
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> {
|
||||||
@ -51,8 +55,17 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
|
|||||||
|
|
||||||
let element_matcher = parser_with_context!(element)(&parser_context);
|
let element_matcher = parser_with_context!(element)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
let (remaining, (children, _exit_contents)) =
|
let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) {
|
||||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
|
||||||
|
remaining,
|
||||||
|
vec![Element::Paragraph(Paragraph::of_text(whitespace))],
|
||||||
|
),
|
||||||
|
Err(_) => {
|
||||||
|
let (remaining, (children, _exit_contents)) =
|
||||||
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||||
|
(remaining, children)
|
||||||
|
}
|
||||||
|
};
|
||||||
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, _trailing_ws) =
|
||||||
|
@ -83,10 +83,18 @@ fn footnote_definition_end<'r, 's>(
|
|||||||
input: &'s str,
|
input: &'s str,
|
||||||
) -> Res<&'s str, &'s str> {
|
) -> Res<&'s str, &'s str> {
|
||||||
let start_of_line_matcher = parser_with_context!(start_of_line)(context);
|
let start_of_line_matcher = parser_with_context!(start_of_line)(context);
|
||||||
let footnote_definition_matcher = parser_with_context!(footnote_definition)(context);
|
let allow_nesting_context =
|
||||||
|
context.with_additional_node(ContextElement::Context("allow nesting footnotes"));
|
||||||
|
let footnote_definition_matcher = parser_with_context!(footnote_definition)(
|
||||||
|
if immediate_in_section(context, "footnote definition") {
|
||||||
|
&allow_nesting_context
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
},
|
||||||
|
);
|
||||||
let maybe_consume_trailing_whitespace_matcher =
|
let maybe_consume_trailing_whitespace_matcher =
|
||||||
parser_with_context!(maybe_consume_trailing_whitespace)(context);
|
parser_with_context!(maybe_consume_trailing_whitespace)(context);
|
||||||
alt((
|
let (remaining, source) = alt((
|
||||||
recognize(tuple((
|
recognize(tuple((
|
||||||
maybe_consume_trailing_whitespace_matcher,
|
maybe_consume_trailing_whitespace_matcher,
|
||||||
footnote_definition_matcher,
|
footnote_definition_matcher,
|
||||||
@ -95,7 +103,9 @@ fn footnote_definition_end<'r, 's>(
|
|||||||
start_of_line_matcher,
|
start_of_line_matcher,
|
||||||
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2),
|
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2),
|
||||||
))),
|
))),
|
||||||
))(input)
|
))(input)?;
|
||||||
|
|
||||||
|
Ok((remaining, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -35,11 +35,6 @@ pub fn greater_block<'r, 's>(
|
|||||||
input: &'s str,
|
input: &'s str,
|
||||||
) -> Res<&'s str, GreaterBlock<'s>> {
|
) -> Res<&'s str, GreaterBlock<'s>> {
|
||||||
// TODO: Do I need to differentiate between different greater block types.
|
// TODO: Do I need to differentiate between different greater block types.
|
||||||
if immediate_in_section(context, "greater block") {
|
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"Cannot nest objects of the same element",
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
start_of_line(context, input)?;
|
start_of_line(context, input)?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
let (remaining, _leading_whitespace) = space0(input)?;
|
||||||
// TODO: Not handling indentation before start of block
|
// TODO: Not handling indentation before start of block
|
||||||
@ -50,11 +45,21 @@ pub fn greater_block<'r, 's>(
|
|||||||
_ => true,
|
_ => true,
|
||||||
}),
|
}),
|
||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
|
let context_name = match name.to_lowercase().as_str() {
|
||||||
|
"center" => "center block",
|
||||||
|
"quote" => "quote block",
|
||||||
|
_ => "greater block",
|
||||||
|
};
|
||||||
|
if immediate_in_section(context, context_name) {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Cannot nest objects of the same element",
|
||||||
|
))));
|
||||||
|
}
|
||||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
|
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
|
||||||
let (remaining, _nl) = line_ending(remaining)?;
|
let (remaining, _nl) = line_ending(remaining)?;
|
||||||
let parser_context = context
|
let parser_context = context
|
||||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||||
.with_additional_node(ContextElement::Context("greater block"))
|
.with_additional_node(ContextElement::Context(context_name))
|
||||||
.with_additional_node(ContextElement::GreaterBlock(name))
|
.with_additional_node(ContextElement::GreaterBlock(name))
|
||||||
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Alpha,
|
class: ExitClass::Alpha,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
:firstdrawer:
|
|
||||||
:seconddrawer:
|
|
||||||
foo
|
foo
|
||||||
:end:
|
:drawername:
|
||||||
|
|
||||||
|
|
||||||
:end:
|
:end:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user