diff --git a/src/elements.rs b/src/elements.rs index 9689f7f..e19dbd9 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -1,6 +1,29 @@ use std::sync::{Arc, Mutex}; -#[derive(Clone, Debug)] +pub const SECTION: &str = "section"; +pub const PARAGRAPH: &str = "paragraph"; +pub const LIST: &str = "list"; +pub const TABLE: &str = "table"; +pub const CODE_BLOCK: &str = "code_block"; +pub const QUOTE: &str = "quote"; +pub const IMPORT: &str = "import"; + +macro_rules! test_block { + ($block:expr, $block_type:expr) => { + match $block { + Block::Section(_) if $block_type == SECTION => true, + Block::List(_) if $block_type == LIST => true, + Block::Table(_) if $block_type == TABLE => true, + Block::Paragraph(_) if $block_type == PARAGRAPH => true, + Block::CodeBlock(_) if $block_type == CODE_BLOCK => true, + Block::Quote(_) if $block_type == QUOTE => true, + Block::Import(_) if $block_type == IMPORT => true, + _ => false, + } + }; +} + +#[derive(Clone, Debug, PartialEq)] pub enum Block { Section(Section), Paragraph(Paragraph), @@ -11,42 +34,42 @@ pub enum Block { Import(Import), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Inline { Text(Text), Ruler(Ruler), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Document { pub(crate) elements: Vec, pub(crate) is_root: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Section { pub(crate) header: Header, pub(crate) elements: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Header { pub(crate) size: u8, pub(crate) line: Inline, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Paragraph { pub(crate) elements: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct List { pub(crate) ordered: bool, pub(crate) items: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ListItem { pub(crate) text: Inline, pub(crate) level: u16, @@ -54,34 +77,34 @@ pub struct ListItem { pub(crate) children: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Table { pub(crate) header: Row, pub(crate) rows: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Row { pub(crate) cells: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Cell { pub(crate) text: Inline, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct CodeBlock { pub(crate) language: String, pub(crate) code: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Code { code: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Quote { pub(crate) metadata: Option, pub(crate) text: Vec, @@ -93,25 +116,25 @@ pub struct Import { pub(crate) anchor: Arc>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ImportAnchor { pub(crate) document: Option, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct InlineMetadata { pub(crate) data: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Ruler {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Text { pub subtext: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum SubText { Plain(PlainText), Code(Code), @@ -124,43 +147,43 @@ pub enum SubText { Image(Image), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct PlainText { pub(crate) value: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct BoldText { pub(crate) value: Box, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ItalicText { pub(crate) value: Box, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnderlinedText { pub(crate) value: Box, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct StrikedText { pub(crate) value: Box, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct MonospaceText { pub(crate) value: PlainText, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Url { pub description: Option, pub url: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Image { pub(crate) url: Url, pub(crate) metadata: Option, @@ -179,6 +202,27 @@ impl Document { pub fn add_element(&mut self, element: Block) { self.elements.push(element) } + + pub fn find(&self, block_type: &str, nested: bool) -> Vec<&Block> { + let mut found = Vec::new(); + let mut found_self = self + .elements + .iter() + .filter(|e| { + if nested { + match e { + Block::Section(sec) => found.append(&mut sec.find(block_type, nested)), + _ => {} + } + } + + test_block!(e, block_type) + }) + .collect(); + found.append(&mut found_self); + + found + } } impl Section { @@ -192,6 +236,26 @@ impl Section { pub fn add_element(&mut self, element: Block) { self.elements.push(element) } + + pub fn find(&self, block_type: &str, nested: bool) -> Vec<&Block> { + let mut found = Vec::new(); + let mut found_self = self + .elements + .iter() + .filter(|e| { + if nested { + match e { + Block::Section(sec) => found.append(&mut sec.find(block_type, nested)), + _ => {} + } + } + test_block!(e, block_type) + }) + .collect(); + found.append(&mut found_self); + + found + } } impl Paragraph { @@ -297,3 +361,9 @@ impl ImportAnchor { self.document = Some(document); } } + +impl PartialEq for Import { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} diff --git a/src/format/assets/style.css b/src/format/assets/style.css index 4aba92f..14af537 100644 --- a/src/format/assets/style.css +++ b/src/format/assets/style.css @@ -13,6 +13,30 @@ body { box-shadow: 1em 1em 1em gray; } +h1 { + font-size: 2.2rem; +} + +h2 { + font-size: 1.8rem; +} + +h3 { + font-size: 1.4rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +h6 { + font-size: 0.4rem; +} + img { max-width: 100%; max-height: 100vh; diff --git a/src/parser.rs b/src/parser.rs index 3b29485..dafc340 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -24,22 +24,65 @@ macro_rules! parse_option { #[derive(Debug)] pub struct ParseError { index: usize, + message: Option, } impl Display for ParseError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "{} Parse Error at index {}{}", - Fg(color::Red), - self.index, - style::Reset - ) + if let Some(message) = &self.message { + write!( + f, + "{} Parse Error at index {}: {}{}", + Fg(color::Red), + self.index, + message, + style::Reset + ) + } else { + write!( + f, + "{} Parse Error at index {}{}", + Fg(color::Red), + self.index, + style::Reset + ) + } } } impl Error for ParseError {} impl ParseError { pub fn new(index: usize) -> Self { - Self { index } + Self { + index, + message: None, + } + } + + pub fn new_with_message(index: usize, message: &str) -> Self { + Self { + index, + message: Some(message.to_string()), + } + } + + pub fn set_message(&mut self, message: &str) { + self.message = Some(message.to_string()); + } + + pub fn get_position(&self, content: &str) -> Option<(usize, usize)> { + if content.len() <= self.index { + return None; + } + let split_content = content.split_at(self.index); + let line_number = split_content.0.matches("\n").count() as usize; + let overshoot_position = self.index as isize - split_content.0.len() as isize; + + if let Some(line) = split_content.0.lines().last() { + let inline_position = (line.len() as isize + overshoot_position) as usize; + + Some((line_number, inline_position)) + } else { + None + } } } @@ -48,6 +91,7 @@ pub struct Parser { text: Vec, current_char: char, section_nesting: u8, + sections: Vec, section_return: Option, path: Option, paths: Arc>>, @@ -90,6 +134,7 @@ impl Parser { index: 0, text, current_char, + sections: Vec::new(), section_nesting: 0, section_return: None, path, @@ -100,6 +145,12 @@ impl Parser { } } + fn get_text(&self) -> String { + self.text + .iter() + .fold("".to_string(), |a, b| format!("{}{}", a, b)) + } + /// Increments the current index and returns the /// char at the indexes position fn next_char(&mut self) -> Option { @@ -117,7 +168,7 @@ impl Parser { self.current_char = char.clone(); Ok(()) } else { - Err(ParseError::new(index)) + Err(ParseError::new_with_message(index, "failed to revert")) } } @@ -242,18 +293,21 @@ impl Parser { path, style::Reset ); - return Err(ParseError::new(self.index)); + return Err(ParseError::new_with_message( + self.index, + "file does not exist", + )); } { let mut paths = self.paths.lock().unwrap(); if paths.iter().find(|item| **item == path) != None { println!( - "{}Import of \"{}\" failed: Cyclic reference.{}", + "{}Import of \"{}\" failed: Cyclic import.{}", Fg(color::Yellow), path, style::Reset ); - return Err(ParseError::new(self.index)); + return Err(ParseError::new_with_message(self.index, "cyclic import")); } paths.push(path.clone()); } @@ -283,7 +337,18 @@ impl Parser { Ok(block) => document.add_element(block), Err(err) => { if let Some(path) = &self.path { - println!("{} Error in File {}: {}", Fg(color::Red), path, err); + if let Some(position) = err.get_position(&self.get_text()) { + println!( + "{} Error in File {}:{}:{} - {}", + Fg(color::Red), + path, + position.0, + position.1, + err + ); + } else { + println!("{} Error in File {}: {}", Fg(color::Red), path, err); + } } else { println!("{}", err); } @@ -302,7 +367,10 @@ impl Parser { pub fn parse_block(&mut self) -> Result { if let Some(section) = self.section_return { if section <= self.section_nesting { - return Err(ParseError::new(self.index)); + return Err(ParseError::new_with_message( + self.index, + "invalid section nesting", + )); } else { self.section_return = None; } @@ -353,6 +421,7 @@ impl Parser { let mut header = self.parse_header()?; header.size = size; self.section_nesting = size; + self.sections.push(size); let mut section = Section::new(header); self.seek_whitespace(); @@ -360,7 +429,12 @@ impl Parser { section.add_element(block); } - self.section_nesting -= 1; + self.sections.pop(); + if let Some(sec) = self.sections.last() { + self.section_nesting = *sec + } else { + self.section_nesting = 0; + } Ok(section) } else { return Err(self.revert_with_error(start_index));