refactor: print errors instead of panicking (#40)

* minor tweaks and error handling

* adding quotes around messages

* restoring test

* Adding a 'sad path' test

* Reverting a change and updating a message.
pull/41/head
James Hodgkinson 1 year ago committed by GitHub
parent 6f5e421ffb
commit ec644c7bf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,6 +13,7 @@ pub mod utils;
const MAX_LINK_NESTED_DEPTH: usize = 10; const MAX_LINK_NESTED_DEPTH: usize = 10;
#[derive(Default)]
pub struct Template; pub struct Template;
impl Template { impl Template {
@ -38,13 +39,8 @@ impl Preprocessor for Template {
.map(|dir| src_dir.join(dir)) .map(|dir| src_dir.join(dir))
.expect("All book items have a parent"); .expect("All book items have a parent");
let content = replace_template( let content =
&chapter.content, replace_template(&chapter.content, &SystemFileReader, base, source, 0);
&SystemFileReader::default(),
base,
source,
0,
);
chapter.content = content; chapter.content = content;
} }
} }
@ -79,7 +75,7 @@ where
for link in links::extract_template_links(chapter_content) { for link in links::extract_template_links(chapter_content) {
replaced.push_str(&chapter_content[previous_end_index..link.start_index]); replaced.push_str(&chapter_content[previous_end_index..link.start_index]);
match link.replace_args(&path, file_reader) { match link.replace_args(path, file_reader) {
Ok(new_content) => { Ok(new_content) => {
if depth < MAX_LINK_NESTED_DEPTH { if depth < MAX_LINK_NESTED_DEPTH {
if let Some(rel_path) = link.link_type.relative_path(path) { if let Some(rel_path) = link.link_type.relative_path(path) {
@ -202,8 +198,8 @@ mod lib_tests {
let start_chapter_content = r" let start_chapter_content = r"
{{#template header.md title=Example Title}} {{#template header.md title=Example Title}}
Some content... Some content...
{{#template {{#template
footer.md footer.md
authors=Goudham & Hazel}}"; authors=Goudham & Hazel}}";
let end_chapter_content = r" let end_chapter_content = r"
# Example Title # Example Title
@ -296,4 +292,24 @@ mod lib_tests {
assert_eq!(actual_chapter_content, start_chapter_content); assert_eq!(actual_chapter_content, start_chapter_content);
} }
#[test]
fn test_sad_path_bad_template() {
let start_chapter_content = [
"This is {{#template template.md",
"text=valid text",
"this has no key for the value and is going to break things}}",
]
.join("\n");
let end_chapter_content = "This is valid text";
let file_name: PathBuf = PathBuf::from("template.md");
let template_file_contents = "[[#text]]".to_string();
let map = HashMap::from([(file_name, template_file_contents)]);
let file_reader = &TestFileReader::from(map);
let actual_chapter_content =
replace_template(&start_chapter_content, file_reader, "", "", 0);
assert_eq!(actual_chapter_content, end_chapter_content);
}
} }

@ -17,26 +17,26 @@ lazy_static! {
// r"(?x)\\\{\{\#.*\}\}|\{\{\s*\#(template)\s+([\S]+)\s*\}\}|\{\{\s*\#(template)\s+([\S]+)\s+([^}]+)\}\}" // r"(?x)\\\{\{\#.*\}\}|\{\{\s*\#(template)\s+([\S]+)\s*\}\}|\{\{\s*\#(template)\s+([\S]+)\s+([^}]+)\}\}"
static ref TEMPLATE: Regex = Regex::new( static ref TEMPLATE: Regex = Regex::new(
r"(?x) # enable insignificant whitespace mode r"(?x) # enable insignificant whitespace mode
\\\{\{ # escaped link opening parens \\\{\{ # escaped link opening parens
\#.* # match any character \#.* # match any character
\}\} # escaped link closing parens \}\} # escaped link closing parens
| # or | # or
\{\{\s* # link opening parens and whitespace(s) \{\{\s* # link opening parens and whitespace(s)
\#(template) # link type - template \#(template) # link type - template
\s+ # separating whitespace \s+ # separating whitespace
([\S]+) # relative path to template file ([\S]+) # relative path to template file
\s* # optional separating whitespaces(s) \s* # optional separating whitespaces(s)
\}\} # link closing parens \}\} # link closing parens
| # or | # or
\{\{\s* # link opening parens and whitespace(s) \{\{\s* # link opening parens and whitespace(s)
\#(template) # link type - template \#(template) # link type - template
\s+ # separating whitespace \s+ # separating whitespace
([\S]+) # relative path to template file ([\S]+) # relative path to template file
\s+ # separating whitespace(s) \s+ # separating whitespace(s)
([^}]+) # get all template arguments ([^}]+) # get all template arguments
\}\} # link closing parens" \}\} # link closing parens"
@ -46,20 +46,20 @@ lazy_static! {
// r"(?x)\\\[\[.*\]\]|\[\[\s*\#([\S]+)\s*\]\]|\[\[\s*\#([\S]+)\s+([^]]+)\]\]" // r"(?x)\\\[\[.*\]\]|\[\[\s*\#([\S]+)\s*\]\]|\[\[\s*\#([\S]+)\s+([^]]+)\]\]"
static ref ARGS: Regex = Regex::new( static ref ARGS: Regex = Regex::new(
r"(?x) # enable insignificant whitespace mode r"(?x) # enable insignificant whitespace mode
\\\[\[ # escaped link opening square brackets \\\[\[ # escaped link opening square brackets
\#.* # match any character \#.* # match any character
\]\] # escaped link closing parens \]\] # escaped link closing parens
| # or | # or
\[\[\s* # link opening parens and whitespace(s) \[\[\s* # link opening parens and whitespace(s)
\#([\S]+) # arg name \#([\S]+) # arg name
\s* # optional separating whitespace(s) \s* # optional separating whitespace(s)
\]\] # link closing parens \]\] # link closing parens
| # or | # or
\[\[\s* # link opening parens and whitespace(s) \[\[\s* # link opening parens and whitespace(s)
\#([\S]+) # arg name \#([\S]+) # arg name
\s+ # optional separating whitespace(s) \s+ # optional separating whitespace(s)
@ -113,23 +113,39 @@ impl<'a> Link<'a> {
.split(LINE_BREAKS) .split(LINE_BREAKS)
.map(|str| str.trim()) .map(|str| str.trim())
.filter(|trimmed| !trimmed.is_empty()) .filter(|trimmed| !trimmed.is_empty())
.map(|mat| { .filter_map(|mat| {
let mut split_n = mat.splitn(2, '='); let mut split_n = mat.splitn(2, '=');
let key = split_n.next().unwrap().trim(); if let Some(key) = split_n.next() {
let value = split_n.next().unwrap(); let key = key.trim();
(key, value) if let Some(value) = split_n.next() {
return Some((key, value));
}
}
eprintln!(
"Couldn't find a key/value pair while parsing the argument '{}'",
mat
);
None
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
// This looks like {{#template <file> <args>}} // This looks like {{#template <file> <args>}}
false => TEMPLATE_ARGS false => TEMPLATE_ARGS
.captures_iter(args.as_str()) .captures_iter(args.as_str())
.into_iter() .filter_map(|mat| {
.map(|mat| { let captures = mat.ok()?;
let mut split_n = mat.unwrap().get(0).unwrap().as_str().splitn(2, '='); let mut split_n = captures.get(0)?.as_str().splitn(2, '=');
let key = split_n.next().unwrap().trim(); if let Some(key) = split_n.next() {
let value = split_n.next().unwrap(); let key = key.trim();
(key, value) if let Some(value) = split_n.next() {
return Some((key.trim(), value));
}
}
eprintln!(
"Couldn't parse key or value while parsing '{:?}'",
&args.as_str()
);
None
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
}; };
@ -157,7 +173,7 @@ impl<'a> Link<'a> {
FR: FileReader, FR: FileReader,
{ {
match self.link_type { match self.link_type {
LinkType::Escaped => Ok((&self.link_text[1..]).to_owned()), LinkType::Escaped => Ok((self.link_text[1..]).to_owned()),
LinkType::Template(ref pat) => { LinkType::Template(ref pat) => {
let target = base.as_ref().join(pat); let target = base.as_ref().join(pat);
let contents = file_reader.read_to_string(&target, self.link_text)?; let contents = file_reader.read_to_string(&target, self.link_text)?;
@ -195,7 +211,7 @@ impl<'a> Iterator for LinkIter<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
for cap in &mut self.0 { for cap in &mut self.0 {
if let Some(inc) = Link::from_capture(cap.unwrap()) { if let Some(inc) = Link::from_capture(cap.ok()?) {
return Some(inc); return Some(inc);
} }
} }
@ -499,11 +515,14 @@ year=2022
#[test] #[test]
fn test_extract_template_links_with_newlines_malformed() { fn test_extract_template_links_with_newlines_malformed() {
let s = "{{#template test.rs let s = [
lang=rust "{{#template test.rs \n",
year=2022}}"; " lang=rust\n",
" year=2022}}",
let res = extract_template_links(s).collect::<Vec<_>>(); ]
.concat();
let res = extract_template_links(&s).collect::<Vec<_>>();
assert_eq!( assert_eq!(
res, res,

Loading…
Cancel
Save