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_name; | ||||
| use crate::parser::sexp::Token; | ||||
| @ -58,6 +60,7 @@ use crate::parser::Timestamp; | ||||
| use crate::parser::Underline; | ||||
| use crate::parser::Verbatim; | ||||
| use crate::parser::VerseBlock; | ||||
| use crate::parser::sexp::unquote; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct DiffResult { | ||||
| @ -323,6 +326,7 @@ fn compare_heading<'s>( | ||||
|     let children = emacs.as_list()?; | ||||
|     let mut child_status = Vec::new(); | ||||
|     let mut this_status = DiffStatus::Good; | ||||
|     let mut message = None; | ||||
|     let emacs_name = "headline"; | ||||
|     if assert_name(emacs, emacs_name).is_err() { | ||||
|         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()) { | ||||
|         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()) { | ||||
|         match rust_child { | ||||
| @ -364,11 +379,38 @@ fn compare_heading<'s>( | ||||
|     Ok(DiffResult { | ||||
|         status: this_status, | ||||
|         name: emacs_name.to_owned(), | ||||
|         message: None, | ||||
|         message, | ||||
|         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>( | ||||
|     source: &'s str, | ||||
|     emacs: &'s Token<'s>, | ||||
| @ -1027,7 +1069,7 @@ fn compare_plain_text<'s>( | ||||
|             rust.source.len() | ||||
|         )); | ||||
|     } | ||||
|     let unquoted_text = text.unquote()?; | ||||
|     let unquoted_text = unquote(text.text)?; | ||||
|     if unquoted_text != rust.source { | ||||
|         this_status = DiffStatus::Bad; | ||||
|         message = Some(format!( | ||||
|  | ||||
| @ -35,44 +35,6 @@ pub struct TextWithProperties<'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 { | ||||
|     Normal, | ||||
|     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"))] | ||||
| pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { | ||||
|     let (remaining, _) = multispace0(input)?; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander