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;
#[derive(Default)]
pub struct Template;
impl Template {
@ -38,13 +39,8 @@ impl Preprocessor for Template {
.map(|dir| src_dir.join(dir))
.expect("All book items have a parent");
let content = replace_template(
&chapter.content,
&SystemFileReader::default(),
base,
source,
0,
);
let content =
replace_template(&chapter.content, &SystemFileReader, base, source, 0);
chapter.content = content;
}
}
@ -79,7 +75,7 @@ where
for link in links::extract_template_links(chapter_content) {
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) => {
if depth < MAX_LINK_NESTED_DEPTH {
if let Some(rel_path) = link.link_type.relative_path(path) {
@ -202,8 +198,8 @@ mod lib_tests {
let start_chapter_content = r"
{{#template header.md title=Example Title}}
Some content...
{{#template
footer.md
{{#template
footer.md
authors=Goudham & Hazel}}";
let end_chapter_content = r"
# Example Title
@ -296,4 +292,24 @@ mod lib_tests {
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+([^}]+)\}\}"
static ref TEMPLATE: Regex = Regex::new(
r"(?x) # enable insignificant whitespace mode
\\\{\{ # escaped link opening parens
\#.* # match any character
\}\} # escaped link closing parens
| # or
\{\{\s* # link opening parens and whitespace(s)
\#(template) # link type - template
\s+ # separating whitespace
([\S]+) # relative path to template file
\s* # optional separating whitespaces(s)
\}\} # link closing parens
\}\} # link closing parens
| # or
\{\{\s* # link opening parens and whitespace(s)
\#(template) # link type - template
\s+ # separating whitespace
([\S]+) # relative path to template file
([\S]+) # relative path to template file
\s+ # separating whitespace(s)
([^}]+) # get all template arguments
\}\} # link closing parens"
@ -46,20 +46,20 @@ lazy_static! {
// r"(?x)\\\[\[.*\]\]|\[\[\s*\#([\S]+)\s*\]\]|\[\[\s*\#([\S]+)\s+([^]]+)\]\]"
static ref ARGS: Regex = Regex::new(
r"(?x) # enable insignificant whitespace mode
\\\[\[ # escaped link opening square brackets
\#.* # match any character
\]\] # escaped link closing parens
| # or
\[\[\s* # link opening parens and whitespace(s)
\#([\S]+) # arg name
\s* # optional separating whitespace(s)
\]\] # link closing parens
| # or
\[\[\s* # link opening parens and whitespace(s)
\#([\S]+) # arg name
\s+ # optional separating whitespace(s)
@ -113,23 +113,39 @@ impl<'a> Link<'a> {
.split(LINE_BREAKS)
.map(|str| str.trim())
.filter(|trimmed| !trimmed.is_empty())
.map(|mat| {
.filter_map(|mat| {
let mut split_n = mat.splitn(2, '=');
let key = split_n.next().unwrap().trim();
let value = split_n.next().unwrap();
(key, value)
if let Some(key) = split_n.next() {
let key = key.trim();
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<_>>(),
// This looks like {{#template <file> <args>}}
false => TEMPLATE_ARGS
.captures_iter(args.as_str())
.into_iter()
.map(|mat| {
let mut split_n = mat.unwrap().get(0).unwrap().as_str().splitn(2, '=');
let key = split_n.next().unwrap().trim();
let value = split_n.next().unwrap();
(key, value)
.filter_map(|mat| {
let captures = mat.ok()?;
let mut split_n = captures.get(0)?.as_str().splitn(2, '=');
if let Some(key) = split_n.next() {
let key = key.trim();
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<_>>(),
};
@ -157,7 +173,7 @@ impl<'a> Link<'a> {
FR: FileReader,
{
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) => {
let target = base.as_ref().join(pat);
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> {
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);
}
}
@ -499,11 +515,14 @@ year=2022
#[test]
fn test_extract_template_links_with_newlines_malformed() {
let s = "{{#template test.rs
lang=rust
year=2022}}";
let res = extract_template_links(s).collect::<Vec<_>>();
let s = [
"{{#template test.rs \n",
" lang=rust\n",
" year=2022}}",
]
.concat();
let res = extract_template_links(&s).collect::<Vec<_>>();
assert_eq!(
res,

Loading…
Cancel
Save