Compare heading tags.
This commit is contained in:
parent
be6197e4c7
commit
9cc5e63c1b
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::util::assert_bounds;
|
use super::util::assert_bounds;
|
||||||
use super::util::assert_name;
|
use super::util::assert_name;
|
||||||
use crate::parser::sexp::Token;
|
use crate::parser::sexp::Token;
|
||||||
@ -58,6 +60,7 @@ use crate::parser::Timestamp;
|
|||||||
use crate::parser::Underline;
|
use crate::parser::Underline;
|
||||||
use crate::parser::Verbatim;
|
use crate::parser::Verbatim;
|
||||||
use crate::parser::VerseBlock;
|
use crate::parser::VerseBlock;
|
||||||
|
use crate::parser::sexp::unquote;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DiffResult {
|
pub struct DiffResult {
|
||||||
@ -323,6 +326,7 @@ fn compare_heading<'s>(
|
|||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let mut message = None;
|
||||||
let emacs_name = "headline";
|
let emacs_name = "headline";
|
||||||
if assert_name(emacs, emacs_name).is_err() {
|
if assert_name(emacs, emacs_name).is_err() {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
@ -347,8 +351,19 @@ fn compare_heading<'s>(
|
|||||||
for (emacs_child, rust_child) in title.as_list()?.iter().zip(rust.title.iter()) {
|
for (emacs_child, rust_child) in title.as_list()?.iter().zip(rust.title.iter()) {
|
||||||
child_status.push(compare_object(source, emacs_child, rust_child)?);
|
child_status.push(compare_object(source, emacs_child, rust_child)?);
|
||||||
}
|
}
|
||||||
|
let emacs_tags = get_tags_from_heading(emacs)?;
|
||||||
|
let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect();
|
||||||
|
let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect();
|
||||||
|
let difference: Vec<&str> = emacs_tags
|
||||||
|
.symmetric_difference(&rust_tags)
|
||||||
|
.map(|val| *val)
|
||||||
|
.collect();
|
||||||
|
if !difference.is_empty() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!("Mismatched tags: {}", difference.join(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Compare tags, todo-keyword, level, priority
|
// TODO: Compare todo-keyword, level, priority
|
||||||
|
|
||||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||||
match rust_child {
|
match rust_child {
|
||||||
@ -364,11 +379,38 @@ fn compare_heading<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: emacs_name.to_owned(),
|
name: emacs_name.to_owned(),
|
||||||
message: None,
|
message,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_tags_from_heading<'s>(
|
||||||
|
emacs: &'s Token<'s>,
|
||||||
|
) -> Result<HashSet<String>, 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 tags = attributes_map
|
||||||
|
.get(":tags")
|
||||||
|
.ok_or("Missing :tags attribute.")?;
|
||||||
|
match tags.as_atom() {
|
||||||
|
Ok("nil") => {
|
||||||
|
return Ok(HashSet::new());
|
||||||
|
}
|
||||||
|
Ok(val) => panic!("Unexpected value for tags: {:?}", val),
|
||||||
|
Err(_) => {}
|
||||||
|
};
|
||||||
|
let tags = {
|
||||||
|
let tags = tags.as_list()?;
|
||||||
|
let strings = tags.iter().map(Token::as_atom).collect::<Result<Vec<&str>, _>>()?;
|
||||||
|
strings.into_iter().map(unquote).collect::<Result<HashSet<String>, _>>()?
|
||||||
|
};
|
||||||
|
Ok(tags)
|
||||||
|
}
|
||||||
|
|
||||||
fn compare_paragraph<'s>(
|
fn compare_paragraph<'s>(
|
||||||
source: &'s str,
|
source: &'s str,
|
||||||
emacs: &'s Token<'s>,
|
emacs: &'s Token<'s>,
|
||||||
@ -1027,7 +1069,7 @@ fn compare_plain_text<'s>(
|
|||||||
rust.source.len()
|
rust.source.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let unquoted_text = text.unquote()?;
|
let unquoted_text = unquote(text.text)?;
|
||||||
if unquoted_text != rust.source {
|
if unquoted_text != rust.source {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
message = Some(format!(
|
message = Some(format!(
|
||||||
|
@ -35,44 +35,6 @@ pub struct TextWithProperties<'s> {
|
|||||||
pub properties: Vec<Token<'s>>,
|
pub properties: Vec<Token<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> TextWithProperties<'s> {
|
|
||||||
pub fn unquote(&self) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut out = String::with_capacity(self.text.len());
|
|
||||||
if !self.text.starts_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not start with quote.".into());
|
|
||||||
}
|
|
||||||
if !self.text.ends_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not end with quote.".into());
|
|
||||||
}
|
|
||||||
let interior_text = &self.text[1..(self.text.len() - 1)];
|
|
||||||
let mut state = ParseState::Normal;
|
|
||||||
for current_char in interior_text.chars().into_iter() {
|
|
||||||
state = match (state, current_char) {
|
|
||||||
(ParseState::Normal, '\\') => ParseState::Escape,
|
|
||||||
(ParseState::Normal, _) => {
|
|
||||||
out.push(current_char);
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, 'n') => {
|
|
||||||
out.push('\n');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '\\') => {
|
|
||||||
out.push('\\');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '"') => {
|
|
||||||
out.push('"');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ParseState {
|
enum ParseState {
|
||||||
Normal,
|
Normal,
|
||||||
Escape,
|
Escape,
|
||||||
@ -133,6 +95,42 @@ impl<'s> Token<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut out = String::with_capacity(text.len());
|
||||||
|
if !text.starts_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not start with quote.".into());
|
||||||
|
}
|
||||||
|
if !text.ends_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not end with quote.".into());
|
||||||
|
}
|
||||||
|
let interior_text = &text[1..(text.len() - 1)];
|
||||||
|
let mut state = ParseState::Normal;
|
||||||
|
for current_char in interior_text.chars().into_iter() {
|
||||||
|
state = match (state, current_char) {
|
||||||
|
(ParseState::Normal, '\\') => ParseState::Escape,
|
||||||
|
(ParseState::Normal, _) => {
|
||||||
|
out.push(current_char);
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, 'n') => {
|
||||||
|
out.push('\n');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '\\') => {
|
||||||
|
out.push('\\');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '"') => {
|
||||||
|
out.push('"');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
||||||
let (remaining, _) = multispace0(input)?;
|
let (remaining, _) = multispace0(input)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user