diff --git a/src/main.rs b/src/main.rs index b930388..15d53df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,7 @@ fn main() { let specs = &args[2..]; let src = || std::fs::read_to_string(&args[0]).unwrap(); match args[1].to_lowercase().trim() { - "html" => println!( + "html" => print!( "{}", to::html::page(&parse(&src()).unwrap(), specs, &mut to::html::State::new()) ), @@ -122,7 +122,13 @@ const BODY_REGEX = new RegExp(".*<\\/bo"+"dy>", "misv"); } buf1[i] = src(); let src = unsafe { &*((&buf1[i]) as *const String) }; - buf2[i] = Some(parse(src).unwrap()); + buf2[i] = Some(match parse(src) { + Ok(v) => v, + Err(e) => { + eprintln!("{}", e.display(src)); + continue; + } + }); let doc = unsafe { &*(buf2[i].as_ref().unwrap() as *const Document<'_>) }; i += 1; let mut http = diff --git a/src/parse.rs b/src/parse.rs index 8f161de..2c8ae11 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::doc::{Component, Document, Section, Text}; pub fn parse<'a>(mut src: &'a str) -> Result, ParseError<'a>> { @@ -6,6 +8,8 @@ pub fn parse<'a>(mut src: &'a str) -> Result, ParseError<'a>> { Ok(doc) } +#[derive(Debug)] +pub struct ParseErrorPlus<'a>(ParseErr<'a>, usize, &'a str); #[derive(Debug)] pub struct ParseError<'a>(ParseErr<'a>, usize); #[derive(Debug)] @@ -20,7 +24,7 @@ pub enum ParseErr<'a> { MissingClosing(char), InvalidBacktickBracketCountSpecifier, InvalidBacktickModifier, - SubsectionNotAllowedHere, + SubdocumentNotAllowedHere, FileTitleWithLink, r#__Zzzz(&'a str), } @@ -371,7 +375,7 @@ fn parse_sections<'a>( // => section hashes Some(' ' | '\n') | None => { if !document { - return Err(ParseError(ParseErr::SubsectionNotAllowedHere, pos)); + return Err(ParseError(ParseErr::SubdocumentNotAllowedHere, pos)); } src.reset_to(pos); match parse_document(src.get(), depth + 1) @@ -517,3 +521,67 @@ impl<'a, 'b> Parser<'a, 'b> { } } } + +impl<'a> ParseError<'a> { + pub fn display(self, src: &'a str) -> ParseErrorPlus<'a> { + ParseErrorPlus(self.0, self.1, src) + } +} +impl<'a> Display for ParseErrorPlus<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ln = self + .2 + .char_indices() + .take_while(|(i, _)| *i <= self.1) + .filter(|(_, c)| *c == '\n') + .count(); + write!(f, "Line {}: {}", ln + 1, self.0) + } +} +impl<'a> Display for ParseErr<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseErr::InvalidSectionHash => write!( + f, + "this section hash isn't valid, if you want a title, add a space after your hashtags" + ), + ParseErr::InvalidHeadingDepth => write!(f, "this heading depth isn't allowed here"), + ParseErr::BracketsAtStartOfSectionWithNoLinebreakAfter => write!( + f, + "brackets at the start of a section must be on their own line" + ), + ParseErr::BracketedSectionNotTerminated => { + write!(f, "this bracketed section isn't terminated anywhere") + } + ParseErr::BracketedSectionTerminatedByTooManyBrackets => write!( + f, + "this bracketed section is terminated by more brackets than expected" + ), + ParseErr::IncompleteBacktick => { + write!( + f, + "incomplete backtick, there needs to be something here (or escape the backtick: ``)" + ) + } + ParseErr::MissingClosing(c) => { + write!(f, "couldn't find an appropriate closing {c} bracket") + } + ParseErr::InvalidBacktickBracketCountSpecifier => write!( + f, + "invalid backtick bracket count between ` and the next ascii symbol" + ), + ParseErr::InvalidBacktickModifier => write!( + f, + "invalid backtick modifier between ` and the next bracket" + ), + ParseErr::SubdocumentNotAllowedHere => { + write!(f, "found a subdocument where it isn't allowed") + } + ParseErr::FileTitleWithLink => write!( + f, + "file title (title at the start of the file) cannot have a link" + ), + ParseErr::__Zzzz(_) => unreachable!(), + } + } +} diff --git a/src/to/html.rs b/src/to/html.rs index 9e215ae..3d38808 100644 --- a/src/to/html.rs +++ b/src/to/html.rs @@ -7,15 +7,13 @@ use crate::{ pub struct State<'a> { pub head_to_body: &'a str, - cache_docs: BTreeMap<&'a Document<'a>, String>, - cache_secs: BTreeMap<&'a Section<'a>, String>, + cache: BTreeMap<&'a Section<'a>, String>, } impl<'a> State<'a> { pub fn new() -> Self { State { head_to_body: "\n", - cache_docs: Default::default(), - cache_secs: Default::default(), + cache: Default::default(), } } } @@ -254,11 +252,6 @@ fn doc_to_impl<'a>( out: &mut String, doc_args: &mut DocArgs<'a, '_>, ) { - if let Some(prev) = doc_args.state.cache_docs.get(doc) { - out.push_str(prev); - return; - } - let out_start = out.len(); let depth = doc_args.depth; let end_at = doc_args.max_len.map(|len| out.len() + len); for d in depth..=doc.depth { @@ -285,7 +278,7 @@ fn doc_to_impl<'a>( out.push_str(&format!("{a}\n")); } for section in &doc.sections { - if let Some(prev) = doc_args.state.cache_secs.get(section) { + if let Some(prev) = doc_args.state.cache.get(section) { out.push_str(prev); continue; } @@ -385,16 +378,12 @@ fn doc_to_impl<'a>( out.push_str("\n"); doc_args .state - .cache_secs + .cache .insert(section, out[section_start..].to_owned()); } for _ in depth..=doc.depth { out.push_str("\n"); } - doc_args - .state - .cache_docs - .insert(doc, out[out_start..].to_owned()); } fn text_to<'a: 'b, 'b>(