use std::path::PathBuf; use std::process::Output; use thiserror::Error; /// Define all the kinds of errors any part of this crate can return. #[derive(Debug, Error)] pub enum SubplotError { /// Subplot could not find a file named as a bindings file. #[error("binding file could not be found: {0}: {1}")] BindingsFileNotFound(PathBuf, std::io::Error), /// Subplot could not find a file named as a functions file. #[error("functions file could not be found: {0}: {1}")] FunctionsFileNotFound(PathBuf, anyhow::Error), /// The simple pattern contains a stray { or }. #[error("simple pattern contains a stray {{ or }}")] StrayBraceInSimplePattern(String), /// The simple pattern has regex metacharacters. /// /// Simple patterns are not permitted to have regex metacharacters in them /// unless the pattern is explicitly marked `regex: false` to indicate that /// the binding author understands what they're up to. #[error("simple pattern contains regex metacharacters: {0}")] SimplePatternHasMetaCharacters(String), /// Scenario step does not match a known binding /// /// This may be due to the binding missing entirely, or that the /// step or the binding has a typo, or that a pattern in the /// binding doesn't match what the author thinks it matches. #[error("do not understand binding: {0}")] BindingUnknown(String), /// A binding in the bindings file doesn't specify a known keyword. #[error("binding doesn't specify known keyword: {0}")] BindingWithoutKnownKeyword(String), /// A binding has more than one keyword (given/when/then). #[error("binding has more than one keyword (given/when/then)")] BindingHasManyKeywords(String), /// Subplot tried to use a program, but couldn't feed it data /// /// Subplot uses some helper programs to implement some of its /// functionality, for example the GraphViz dot program. This /// error means that when tried to start a helper program and /// write to the helper's standard input stream, it failed to get /// the stream. /// /// This probably implies there's something wrong on your system. #[error("couldn't get stdin of child process to write to it")] ChildNoStdin, /// Subplot helper program failed /// /// Subplot uses some helper programs to implement some of its /// functionality, for example the GraphViz dot program. This /// error means that the helper program failed (exit code was not /// zero). /// /// This probably implies there's something wrong in Subplot. /// Please report this error to the Subplot project. #[error("child process failed: {0}")] ChildFailed(String), /// Binding doesn't define a function /// /// All binding must define the name of the function that /// implements the step. The bindings file has at least one /// binding that doesn't define one. To fix, add a `function:` /// field to the binding. #[error("binding does not name a function: {0}")] NoFunction(String), /// Document has no title /// /// The document YAML metadata does not define a document title. /// To fix, add a `title` field. #[error("document has no title")] NoTitle, /// Pandoc AST is not JSON /// /// Subplot acts as a Pandoc filter, and as part of that Pandoc /// constructs an _abstract syntax tree_ from the input document, /// and feeds it to the filter as JSON. However, when Subplot was /// parsing the AST, it wasn't JSON. /// /// This probably means there's something wrong with Pandoc, it's /// Rust bindings, or Subplot. #[error("Pandoc produce AST not in JSON")] NotJson, /// First scenario is before first heading /// /// Subplot scenarios are group by the input document's structure. /// Each scenario must be in a chapter, section, subsection, or /// other structural element with a heading. Subplot found a /// scenario block before the first heading in the document. /// /// To fix, add a heading or move the scenario after a heading. #[error("first scenario is before first heading")] ScenarioBeforeHeading, /// Step does not have a keyword. #[error("step has no keyword: {0}")] NoStepKeyword(String), /// Unknown scenario step keyword. /// /// Each scenario step must start with a known keyword (given, /// when, then, and, but), but Subplot didn't find one it /// recognized. /// /// This is usually due to a typing mistake or similar. #[error("unknown step keyword: {0}")] UnknownStepKind(String), /// Scenario step uses continuation keyword too early /// /// If a continuation keyword (`and` or `but`) is used too early /// in a scenario (i.e. before any other keyword was used) then /// it cannot be resolved to whichever keyword it should have been. #[error("continuation keyword used too early")] ContinuationTooEarly, /// Embedded file has the same name as another embedded file /// /// Names of embedded files must be unique in the input document, /// but Subplot found at least one with the same name as another. #[error("Duplicate embedded file name: {0}")] DuplicateEmbeddedFilename(String), /// Embedded file has more than one `add-newline` attribute /// /// The `add-newline` attribute can only be specified once for any given /// embedded file #[error("Embedded file {0} has more than one `add-newline` attribute")] RepeatedAddNewlineAttribute(String), /// Unrecognised `add-newline` attribute value on an embedded file /// /// The `add-newline` attribute can only take the values `auto`, `yes`, /// and `no`. #[error("Embedded file {0} has unrecognised `add-newline={}` - valid values are auto/yes/no")] UnrecognisedAddNewline(String, String), /// Couldn't determine base directory from input file name. /// /// Subplot needs to to determine the base directory for files /// referred to by the markdown input file (e.g., bindings and /// functions files). It failed to do that from the name of the /// input file. Something weird is happening. #[error("Could not determine base directory for included files from {0:?}")] BasedirError(PathBuf), /// Output goes into a directory that does not exist. /// /// Subplot needs to know in which directory it should write its /// output file, since it writes a temporary file first, then /// renames it to the final output file. The temporary file is /// created in the same directory as the final output file. /// However, Subplot could not find that directory. #[error("Output going to a directory that does not exist: {0}")] OutputDirectoryNotFound(String), /// The template.yaml is not in a directory. /// /// Template specifications reference files relative to the /// template.yaml file, but Subplot could not find the name of the /// directory containing the template.yaml file. Something is very /// weird. #[error("Couldn't find name of directory containing template spec: {0}")] NoTemplateSpecDirectory(PathBuf), /// Unknown classes in use in document #[error("Unknown classes found in the document: {0}")] UnknownClasses(String), /// Template does not specify how to run generated program /// /// The template.yaml file used does not specify how to run the /// generated program, but user asked codegen to run it. #[error("template.yaml does not specify how to run generated program")] TemplateNoRun, /// An embedded file was not found. #[error("emedded file {0} was not found in the subplot document")] EmbeddedFileNotFound(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, }, /// 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, }, /// 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, }, /// 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, }, /// 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, }, } impl SubplotError { /// Construct a ChildFailed error. pub fn child_failed(msg: &str, output: &Output) -> SubplotError { let msg = format!( "{}: {}: {:?}", msg, output.status.code().unwrap_or(-1), String::from_utf8_lossy(&output.stderr) ); SubplotError::ChildFailed(msg) } } /// A result type for this crate. pub type Result = std::result::Result;