diff options
author | Daniel Silverstone <dsilvers+gitlab@digital-scurf.org> | 2022-05-06 19:28:34 +0000 |
---|---|---|
committer | Daniel Silverstone <dsilvers+gitlab@digital-scurf.org> | 2022-05-06 19:28:34 +0000 |
commit | 27b617cbaa9e6d4fb58a0b971b428a05da8ec5d9 (patch) | |
tree | 08067bf4290982e4a717ba55a660714084370158 /src | |
parent | c2cbccd45a2c356e458abc8bbef3193434e8572a (diff) | |
parent | 52ccfa5a1bf2893bdc63ff1bad2b2a88ca86ef95 (diff) | |
download | subplot-27b617cbaa9e6d4fb58a0b971b428a05da8ec5d9.tar.gz |
Merge branch 'liw/tidy-up-error' into 'main'
tidy up error handling a bit
See merge request subplot/subplot!275
Diffstat (limited to 'src')
-rw-r--r-- | src/bindings.rs | 26 | ||||
-rw-r--r-- | src/codegen.rs | 13 | ||||
-rw-r--r-- | src/diagrams.rs | 39 | ||||
-rw-r--r-- | src/doc.rs | 65 | ||||
-rw-r--r-- | src/error.rs | 83 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/matches.rs | 10 | ||||
-rw-r--r-- | src/metadata.rs | 17 | ||||
-rw-r--r-- | src/steps.rs | 7 | ||||
-rw-r--r-- | src/templatespec.rs | 10 |
10 files changed, 154 insertions, 117 deletions
diff --git a/src/bindings.rs b/src/bindings.rs index 0837553..5db63c9 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -3,7 +3,7 @@ use super::MatchedSteps; use super::PartialStep; use super::ScenarioStep; use super::StepKind; -use crate::{resource, Result, SubplotError}; +use crate::{resource, SubplotError}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::*; @@ -54,7 +54,7 @@ pub enum CaptureType { impl FromStr for CaptureType { type Err = SubplotError; - fn from_str(value: &str) -> Result<Self> { + fn from_str(value: &str) -> Result<Self, SubplotError> { match value.to_ascii_lowercase().as_str() { "word" => Ok(Self::Word), "text" => Ok(Self::Text), @@ -168,10 +168,11 @@ impl Binding { pattern: &str, case_sensitive: bool, mut types: HashMap<String, CaptureType>, - ) -> Result<Binding> { + ) -> Result<Binding, SubplotError> { let regex = RegexBuilder::new(&format!("^{}$", pattern)) .case_insensitive(!case_sensitive) - .build()?; + .build() + .map_err(|err| SubplotError::Regex(pattern.to_string(), err))?; // For every named capture, ensure we have a known type for it. // If the type is missing from the map, we default to `text` which is // the .* pattern @@ -462,8 +463,9 @@ impl Bindings { } /// Add bindings from a YAML string - pub fn add_from_yaml(&mut self, yaml: &str) -> Result<()> { - let bindings: Vec<ParsedBindingWrapper> = serde_yaml::from_str(yaml)?; + pub fn add_from_yaml(&mut self, yaml: &str) -> Result<(), SubplotError> { + let bindings: Vec<ParsedBindingWrapper> = + serde_yaml::from_str(yaml).map_err(SubplotError::Metadata)?; for wrapper in bindings { self.add(from_hashmap(&wrapper.binding)?); } @@ -477,7 +479,7 @@ impl Bindings { /// Find the binding matching a given scenario step, if there is /// exactly one. - pub fn find(&self, template: &str, step: &ScenarioStep) -> Result<MatchedStep> { + pub fn find(&self, template: &str, step: &ScenarioStep) -> Result<MatchedStep, SubplotError> { let mut matches: Vec<MatchedStep> = self .bindings() .iter() @@ -499,7 +501,11 @@ impl Bindings { } /// Add bindings from a file. - pub fn add_from_file<P>(&mut self, filename: P, template: Option<&str>) -> Result<()> + pub fn add_from_file<P>( + &mut self, + filename: P, + template: Option<&str>, + ) -> Result<(), SubplotError> where P: AsRef<Path> + Debug, { @@ -522,7 +528,7 @@ impl Bindings { } } -fn from_hashmap(parsed: &ParsedBinding) -> Result<Binding> { +fn from_hashmap(parsed: &ParsedBinding) -> Result<Binding, SubplotError> { let given: i32 = parsed.given.is_some().into(); let when: i32 = parsed.when.is_some().into(); let then: i32 = parsed.then.is_some().into(); @@ -796,7 +802,7 @@ fn regex_from_simple_pattern( pattern: &str, explicit_plain: bool, types: &mut HashMap<String, CaptureType>, -) -> Result<String> { +) -> Result<String, SubplotError> { let pat = Regex::new(r"\{[^\s\{\}]+\}").unwrap(); let mut r = String::new(); let mut end = 0; diff --git a/src/codegen.rs b/src/codegen.rs index b940b4d..5c4255f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -62,12 +62,15 @@ fn tera(tmplspec: &TemplateSpec, templatename: &str) -> Result<Tera, SubplotErro let dirname = tmplspec.template_filename().parent().unwrap(); for helper in tmplspec.helpers() { let helper_path = dirname.join(helper); - let helper_content = resource::read_as_string(helper_path, Some(templatename))?; + let helper_content = resource::read_as_string(&helper_path, Some(templatename)) + .map_err(|err| SubplotError::ReadFile(helper_path.clone(), err))?; let helper_name = helper.display().to_string(); tera.add_raw_template(&helper_name, &helper_content) .map_err(|err| SubplotError::TemplateError(helper_name.to_string(), err))?; } - let template = resource::read_as_string(tmplspec.template_filename(), Some(templatename))?; + let path = tmplspec.template_filename(); + let template = resource::read_as_string(path, Some(templatename)) + .map_err(|err| SubplotError::ReadFile(path.to_path_buf(), err))?; tera.add_raw_template("template", &template) .map_err(|err| { SubplotError::TemplateError(tmplspec.template_filename().display().to_string(), err) @@ -76,8 +79,10 @@ fn tera(tmplspec: &TemplateSpec, templatename: &str) -> Result<Tera, SubplotErro } fn write(filename: &Path, content: &str) -> Result<(), SubplotError> { - let mut f: File = File::create(filename)?; - f.write_all(content.as_bytes())?; + let mut f: File = File::create(filename) + .map_err(|err| SubplotError::CreateFile(filename.to_path_buf(), err))?; + f.write_all(content.as_bytes()) + .map_err(|err| SubplotError::WriteFile(filename.to_path_buf(), err))?; Ok(()) } diff --git a/src/diagrams.rs b/src/diagrams.rs index 96f5d70..2bdd0ed 100644 --- a/src/diagrams.rs +++ b/src/diagrams.rs @@ -1,4 +1,4 @@ -use crate::{Result, SubplotError}; +use crate::SubplotError; use std::env; use std::ffi::OsString; @@ -72,7 +72,7 @@ lazy_static! { /// for the trait. pub trait DiagramMarkup { /// Convert the markup into an SVG. - fn as_svg(&self) -> Result<Vec<u8>>; + fn as_svg(&self) -> Result<Vec<u8>, SubplotError>; } /// A code block with pikchr markup. @@ -99,7 +99,7 @@ impl PikchrMarkup { } impl DiagramMarkup for PikchrMarkup { - fn as_svg(&self) -> Result<Vec<u8>> { + fn as_svg(&self) -> Result<Vec<u8>, SubplotError> { let mut flags = pikchr::PikchrFlags::default(); flags.generate_plain_errors(); let image = pikchr::Pikchr::render(&self.markup, self.class.as_deref(), flags) @@ -130,16 +130,22 @@ impl DotMarkup { } impl DiagramMarkup for DotMarkup { - fn as_svg(&self) -> Result<Vec<u8>> { - let mut child = Command::new(DOT_PATH.lock().unwrap().clone()) + fn as_svg(&self) -> Result<Vec<u8>, SubplotError> { + let path = DOT_PATH.lock().unwrap().clone(); + let mut child = Command::new(&path) .arg("-Tsvg") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .spawn()?; + .spawn() + .map_err(|err| SubplotError::Spawn(path.clone(), err))?; if let Some(stdin) = child.stdin.as_mut() { - stdin.write_all(self.markup.as_bytes())?; - let output = child.wait_with_output()?; + stdin + .write_all(self.markup.as_bytes()) + .map_err(SubplotError::WriteToChild)?; + let output = child + .wait_with_output() + .map_err(SubplotError::WaitForChild)?; if output.status.success() { Ok(output.stdout) } else { @@ -191,8 +197,9 @@ impl PlantumlMarkup { } impl DiagramMarkup for PlantumlMarkup { - fn as_svg(&self) -> Result<Vec<u8>> { - let mut cmd = Command::new(JAVA_PATH.lock().unwrap().clone()); + fn as_svg(&self) -> Result<Vec<u8>, SubplotError> { + let path = JAVA_PATH.lock().unwrap().clone(); + let mut cmd = Command::new(&path); cmd.arg("-Djava.awt.headless=true") .arg("-jar") .arg(PLANTUML_JAR_PATH.lock().unwrap().clone()) @@ -208,10 +215,16 @@ impl DiagramMarkup for PlantumlMarkup { if let Some(path) = Self::build_java_path() { cmd.env("PATH", path); } - let mut child = cmd.spawn()?; + let mut child = cmd + .spawn() + .map_err(|err| SubplotError::Spawn(path.clone(), err))?; if let Some(stdin) = child.stdin.as_mut() { - stdin.write_all(self.markup.as_bytes())?; - let output = child.wait_with_output()?; + stdin + .write_all(self.markup.as_bytes()) + .map_err(SubplotError::WriteToChild)?; + let output = child + .wait_with_output() + .map_err(SubplotError::WaitForChild)?; if output.status.success() { Ok(output.stdout) } else { @@ -11,8 +11,8 @@ use crate::PartialStep; use crate::Scenario; use crate::ScenarioStep; use crate::Style; +use crate::SubplotError; use crate::{bindings::CaptureType, parser::parse_scenario_snippet}; -use crate::{Result, SubplotError}; use crate::{Warning, Warnings}; use std::collections::HashSet; @@ -118,7 +118,7 @@ impl<'a> Document { mut ast: Pandoc, style: Style, template: Option<&str>, - ) -> Result<Document> + ) -> Result<Document, SubplotError> where P: AsRef<Path> + Debug, { @@ -143,12 +143,12 @@ impl<'a> Document { json: &str, style: Style, template: Option<&str>, - ) -> Result<Document> + ) -> Result<Document, SubplotError> where P: AsRef<Path> + Debug, { trace!("Parsing document..."); - let ast: Pandoc = serde_json::from_str(json)?; + let ast: Pandoc = serde_json::from_str(json).map_err(SubplotError::AstJson)?; Self::from_ast(basedir, markdowns, ast, style, template) } @@ -162,7 +162,7 @@ impl<'a> Document { filename: &Path, style: Style, template: Option<&str>, - ) -> Result<Document> { + ) -> Result<Document, SubplotError> { trace!( "Document::from_file: basedir={} filename={}", basedir.display(), @@ -183,7 +183,7 @@ impl<'a> Document { crate::policy::add_citeproc(&mut pandoc); trace!("Invoking Pandoc to parse document {:?}", filename); - let output = match pandoc.execute()? { + let output = match pandoc.execute().map_err(SubplotError::Pandoc)? { pandoc::PandocOutput::ToBuffer(o) => o, _ => return Err(SubplotError::NotJson), }; @@ -203,10 +203,11 @@ impl<'a> Document { filename: &Path, style: Style, template: Option<&str>, - ) -> Result<Document> { + ) -> Result<Document, SubplotError> { trace!("Parsing document with pullmark-cmark from {:?}", filename); let filename = filename.to_path_buf(); - let markdown = std::fs::read_to_string(&filename)?; + let markdown = std::fs::read_to_string(&filename) + .map_err(|err| SubplotError::ReadFile(filename.clone(), err))?; let ast = ast::AbstractSyntaxTree::from_str(&markdown)?; trace!("Parsed document OK"); @@ -217,8 +218,8 @@ impl<'a> Document { /// /// This is useful in a Pandoc filter, so that the filter can give /// it back to Pandoc for typesetting. - pub fn ast(&self) -> Result<String> { - let json = serde_json::to_string(&self.ast)?; + pub fn ast(&self) -> Result<String, SubplotError> { + let json = serde_json::to_string(&self.ast).map_err(SubplotError::AstJson)?; Ok(json) } @@ -277,7 +278,7 @@ impl<'a> Document { } /// Check the document for common problems. - pub fn lint(&self) -> Result<()> { + pub fn lint(&self) -> Result<(), SubplotError> { trace!("Linting document"); self.check_doc_has_title()?; self.check_filenames_are_unique()?; @@ -287,7 +288,7 @@ impl<'a> Document { } // Check that all filenames for embedded files are unique. - fn check_filenames_are_unique(&self) -> Result<()> { + fn check_filenames_are_unique(&self) -> Result<(), SubplotError> { let mut known = HashSet::new(); for filename in self.files().iter().map(|f| f.filename().to_lowercase()) { if known.contains(&filename) { @@ -299,7 +300,7 @@ impl<'a> Document { } // Check that document has a title in its metadata. - fn check_doc_has_title(&self) -> Result<()> { + fn check_doc_has_title(&self) -> Result<(), SubplotError> { if self.meta().title().is_empty() { Err(SubplotError::NoTitle) } else { @@ -308,7 +309,7 @@ impl<'a> Document { } /// Check that all the block classes in the document are known - fn check_block_classes(&self) -> Result<()> { + fn check_block_classes(&self) -> Result<(), SubplotError> { let mut visitor = visitor::BlockClassVisitor::default(); // Irritatingly we can't immutably visit the AST for some reason // This clone() is expensive and unwanted, but I'm not sure how @@ -341,7 +342,7 @@ impl<'a> Document { /// Check that all named files (in matched steps) are actually present in the /// document. - pub fn check_named_files_exist(&mut self, template: &str) -> Result<bool> { + pub fn check_named_files_exist(&mut self, template: &str) -> Result<bool, SubplotError> { let filenames: HashSet<_> = self .files() .iter() @@ -374,7 +375,7 @@ impl<'a> Document { } /// Check that all embedded files are used by matched steps. - pub fn check_embedded_files_are_used(&mut self, template: &str) -> Result<bool> { + pub fn check_embedded_files_are_used(&mut self, template: &str) -> Result<bool, SubplotError> { let mut filenames: HashSet<_> = self .files() .iter() @@ -442,7 +443,7 @@ impl<'a> Document { } /// Return all scenarios in a document. - pub fn scenarios(&mut self) -> Result<Vec<Scenario>> { + pub fn scenarios(&mut self) -> Result<Vec<Scenario>, SubplotError> { let mut visitor = visitor::StructureVisitor::new(); visitor.walk_pandoc(&mut self.ast); @@ -460,7 +461,10 @@ impl<'a> Document { } /// Return matched scenarios in a document. - pub fn matched_scenarios(&mut self, template: &str) -> Result<Vec<MatchedScenario>> { + pub fn matched_scenarios( + &mut self, + template: &str, + ) -> Result<Vec<MatchedScenario>, SubplotError> { let scenarios = self.scenarios()?; trace!( "Found {} scenarios, checking their bindings", @@ -474,7 +478,7 @@ impl<'a> Document { } /// Extract a template name from this document - pub fn template(&self) -> Result<&str> { + pub fn template(&self) -> Result<&str, SubplotError> { let templates: Vec<_> = self.meta().templates().collect(); if templates.len() == 1 { Ok(templates[0]) @@ -489,7 +493,11 @@ impl<'a> Document { /// Load a `Document` from a file. /// /// This version uses Pandoc to parse the Markdown. -pub fn load_document<P>(filename: P, style: Style, template: Option<&str>) -> Result<Document> +pub fn load_document<P>( + filename: P, + style: Style, + template: Option<&str>, +) -> Result<Document, SubplotError> where P: AsRef<Path> + Debug, { @@ -514,7 +522,7 @@ pub fn load_document_with_pullmark<P>( filename: P, style: Style, template: Option<&str>, -) -> Result<Document> +) -> Result<Document, SubplotError> where P: AsRef<Path> + Debug, { @@ -533,7 +541,11 @@ where } /// Generate code for one document. -pub fn codegen(filename: &Path, output: &Path, template: Option<&str>) -> Result<CodegenOutput> { +pub fn codegen( + filename: &Path, + output: &Path, + template: Option<&str>, +) -> Result<CodegenOutput, SubplotError> { let r = load_document_with_pullmark(filename, Style::default(), template); let mut doc = match r { Ok(doc) => doc, @@ -577,7 +589,7 @@ impl CodegenOutput { } } -fn extract_scenario(e: &[visitor::Element]) -> Result<(Option<Scenario>, usize)> { +fn extract_scenario(e: &[visitor::Element]) -> Result<(Option<Scenario>, usize), SubplotError> { if e.is_empty() { // If we get here, it's a programming error. panic!("didn't expect empty list of elements"); @@ -625,7 +637,6 @@ fn extract_scenario(e: &[visitor::Element]) -> Result<(Option<Scenario>, usize)> mod test_extract { use super::extract_scenario; use super::visitor::Element; - use crate::Result; use crate::Scenario; use crate::SubplotError; @@ -637,7 +648,11 @@ mod test_extract { Element::Snippet(text.to_string()) } - fn check_result(r: Result<(Option<Scenario>, usize)>, title: Option<&str>, i: usize) { + fn check_result( + r: Result<(Option<Scenario>, usize), SubplotError>, + title: Option<&str>, + i: usize, + ) { assert!(r.is_ok()); let (actual_scen, actual_i) = r.unwrap(); if title.is_none() { diff --git a/src/error.rs b/src/error.rs index c93141e..a42298d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -259,60 +259,48 @@ pub enum SubplotError { #[error("no scenarios were found matching the `{0}` template")] NoScenariosMatched(String), - /// I/O error - /// - /// Subplot did some I/O, and it failed. This is a generic wrapper - /// for any kind of I/O error. - #[error(transparent)] - IoError { - /// The wrapped error. - #[from] - source: std::io::Error, - }, + /// Failed to invoke a program. + #[error("Failed to invoke {0}")] + Spawn(PathBuf, #[source] std::io::Error), - /// Pandoc error - /// - /// Subplot got an error from Panoc. This is a generic wrapper for - /// any kinds of Pandoc errors. - #[error(transparent)] - PandocError { - /// The wrapped error. - #[from] - source: pandoc::PandocError, - }, + /// Failed to write to stdin of child process. + #[error("Failed to write to stdin of child process")] + WriteToChild(#[source] std::io::Error), + + /// Error when waiting for child process to finish. + #[error("Error when waiting for child process to finish")] + WaitForChild(#[source] std::io::Error), + + /// Error when reading a file. + #[error("Error when reading {0}")] + ReadFile(PathBuf, #[source] std::io::Error), + + /// Error when creating a file. + #[error("Error when creating {0}")] + CreateFile(PathBuf, #[source] std::io::Error), + + /// Error when writing to a file. + #[error("Error when writing to {0}")] + WriteFile(PathBuf, #[source] std::io::Error), + + /// Error executing Pandoc. + #[error("Pandoc failed")] + Pandoc(#[source] pandoc::PandocError), /// Regular expression error /// /// Subplot uses regular expressions. This is a generic wrapper for /// any kinds of errors related to that. - #[error(transparent)] - RegexError { - /// The wrapped error. - #[from] - source: regex::Error, - }, + #[error("Failed to compile regular expression: {0:?}")] + Regex(String, #[source] regex::Error), - /// JSON error - /// - /// Subplot parses and generates JSON. This is a generic wrapper - /// for any kinds of errors related to that. - #[error(transparent)] - JsonError { - /// The wrapped error. - #[from] - source: serde_json::Error, - }, + /// Error parsing the Pandoc abstract syntax tree as JSON. + #[error("Failed to parse document AST as JSON")] + AstJson(#[source] serde_json::Error), - /// YAML error - /// - /// Subplot parses YAML. This is a generic wrapper for any kinds - /// of errors related to that. - #[error(transparent)] - YamlError { - /// The wrapped error. - #[from] - source: serde_yaml::Error, - }, + /// Error parsing YAML metadata for document. + #[error("Failed to parse YAML metadata")] + Metadata(#[source] serde_yaml::Error), /// Abstract syntax tree error. #[error(transparent)] @@ -336,9 +324,6 @@ impl SubplotError { } } -/// A result type for this crate. -pub type Result<T> = std::result::Result<T, SubplotError>; - /// A warning, or non-fatal error. /// /// Errors prevent Subplot from producing output. Warnings don't do that. @@ -14,7 +14,6 @@ extern crate pandoc_ast_07 as pandoc_ast; extern crate pandoc_ast_08 as pandoc_ast; mod error; -pub use error::Result; pub use error::SubplotError; pub use error::Warning; pub use error::Warnings; diff --git a/src/matches.rs b/src/matches.rs index f8e527a..9130641 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -1,7 +1,7 @@ use crate::Binding; -use crate::Result; use crate::Scenario; use crate::StepKind; +use crate::SubplotError; use crate::{bindings::CaptureType, Bindings}; use std::collections::HashMap; @@ -17,8 +17,12 @@ pub struct MatchedScenario { impl MatchedScenario { /// Construct a new matched scenario - pub fn new(template: &str, scen: &Scenario, bindings: &Bindings) -> Result<MatchedScenario> { - let steps: Result<Vec<MatchedStep>> = scen + pub fn new( + template: &str, + scen: &Scenario, + bindings: &Bindings, + ) -> Result<MatchedScenario, SubplotError> { + let steps: Result<Vec<MatchedStep>, SubplotError> = scen .steps() .iter() .map(|step| bindings.find(template, step)) diff --git a/src/metadata.rs b/src/metadata.rs index 0248891..5f5e183 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,5 +1,4 @@ -use crate::Result; -use crate::{Bindings, TemplateSpec}; +use crate::{Bindings, SubplotError, TemplateSpec}; use std::collections::HashMap; use std::fmt::Debug; @@ -31,7 +30,11 @@ pub struct DocumentImpl { impl Metadata { /// Construct a Metadata from a Document, if possible. - pub fn new<P>(basedir: P, doc: &Pandoc, template: Option<&str>) -> Result<Metadata> + pub fn new<P>( + basedir: P, + doc: &Pandoc, + template: Option<&str>, + ) -> Result<Metadata, SubplotError> where P: AsRef<Path> + Debug, { @@ -152,7 +155,7 @@ fn get_bindings_filenames(map: &Mapp) -> Vec<PathBuf> { get_paths("", map, "bindings") } -fn load_template_spec(template: &str) -> Result<TemplateSpec> { +fn load_template_spec(template: &str) -> Result<TemplateSpec, SubplotError> { let mut spec_path = PathBuf::from(template); spec_path.push("template"); spec_path.push("template.yaml"); @@ -298,7 +301,11 @@ mod test_join { } } -fn get_bindings<P>(filenames: &[P], bindings: &mut Bindings, template: Option<&str>) -> Result<()> +fn get_bindings<P>( + filenames: &[P], + bindings: &mut Bindings, + template: Option<&str>, +) -> Result<(), SubplotError> where P: AsRef<Path> + Debug, { diff --git a/src/steps.rs b/src/steps.rs index c8c1bf6..ccbc588 100644 --- a/src/steps.rs +++ b/src/steps.rs @@ -1,4 +1,4 @@ -use crate::{Result, SubplotError}; +use crate::SubplotError; use serde::{Deserialize, Serialize}; use std::fmt; @@ -47,7 +47,10 @@ impl ScenarioStep { /// /// If the step uses the "and" or "but" keyword, use the default /// step kind instead. - pub fn new_from_str(text: &str, default: Option<StepKind>) -> Result<ScenarioStep> { + pub fn new_from_str( + text: &str, + default: Option<StepKind>, + ) -> Result<ScenarioStep, SubplotError> { let mut words = text.split_whitespace(); let keyword = match words.next() { diff --git a/src/templatespec.rs b/src/templatespec.rs index 5d7150b..28ab7e1 100644 --- a/src/templatespec.rs +++ b/src/templatespec.rs @@ -1,5 +1,4 @@ use crate::resource; -use crate::Result; use crate::SubplotError; use serde::Deserialize; @@ -21,8 +20,8 @@ pub struct TemplateSpec { impl TemplateSpec { // Create a new TemplateSpec from YAML text. - fn from_yaml(yaml: &str) -> Result<TemplateSpec> { - Ok(serde_yaml::from_str(yaml)?) + fn from_yaml(yaml: &str) -> Result<TemplateSpec, SubplotError> { + serde_yaml::from_str(yaml).map_err(SubplotError::Metadata) } // Create a new TemplateSpec. @@ -40,8 +39,9 @@ impl TemplateSpec { } /// Read a template.yaml file and create the corresponding TemplateSpec. - pub fn from_file(filename: &Path) -> Result<TemplateSpec> { - let yaml = resource::read_as_string(filename, None)?; + pub fn from_file(filename: &Path) -> Result<TemplateSpec, SubplotError> { + let yaml = resource::read_as_string(filename, None) + .map_err(|err| SubplotError::ReadFile(filename.to_path_buf(), err))?; let spec = TemplateSpec::from_yaml(&yaml)?; let dirname = match filename.parent() { Some(x) => x, |