Commits: 8
Introduce an intentionally wrong step for testing
index c36e714..101d5af 100644
--- a/samples/basic.md
+++ b/samples/basic.md
@@ -25,4 +25,8 @@ Content outside of bullet points (like this) won't have any effect on the progra
= * The word `blocks` has `6` characters
= * There are `3` `r`s in the word `strawberry`
= * The reverse of `abc` is `cba`
+ * The reverse of `CIA` is `KGB`
+
+ This step is intentionally wrong to allow demonstrate that TBB will not proceed with following steps.
+
= * There are `2` `o`s in the word `boost`Setup default package output from Nix flake
So that this wonder can be used in other projects.
index 98507b2..727883b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
=.devenv
=.direnv/
+result
=
=# Added by cargo
=index b93a9d3..3807d0f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -40,5 +40,22 @@
= ];
= };
= });
+
+ packages = forEachSystem
+ (system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ cargo-config = builtins.fromTOML (builtins.readFile ./Cargo.toml);
+ in
+ {
+ default = pkgs.rustPlatform.buildRustPackage {
+ pname = cargo-config.package.name;
+ version = cargo-config.package.version;
+ src = ./.;
+ cargoLock = {
+ lockFile = ./Cargo.lock;
+ };
+ };
+ });
= };
=}Update the roadmap
index cb1c00d..aed3aea 100644
--- a/README.md
+++ b/README.md
@@ -35,16 +35,17 @@ Support for Linux, OS X and Web (WASM).
= - [x] Interpretter in a different language (Python)
= - [x] Report why steps fail
=- [ ] More readable report
- - [ ] The emojis are misaligned and lack color (at least in my terminal)
+ - [x] The emojis are misaligned and lack color (at least in my terminal)
= - [ ] A summary at the bottom (esp. list of errors)
- - [ ] Use colors
+ - [x] Use colors
+ - [ ] More instructive error messages
=- [ ] Pass more step data to interpreters
= - [ ] Code blocks
= - [ ] Lists
= - [ ] Tables
= - [ ] Definition lists
= - [ ] Original markdown fragment
-- [ ] Nix package (from Flake)
+- [x] Nix package (from Flake)
=- [ ] Use for evaluating Jewiet's Form to Mail specification
=- [ ] Helper libraries
= - [ ] for PythonUse anyhow to manage errors
index 9c1b9d0..2811a83 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -61,6 +61,12 @@ dependencies = [
= "windows-sys 0.61.2",
=]
=
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
=[[package]]
=name = "clap"
=version = "4.5.51"
@@ -381,6 +387,7 @@ dependencies = [
=name = "tad-better-behavior"
=version = "0.1.0"
=dependencies = [
+ "anyhow",
= "clap",
= "colored",
= "env_logger",index 3ffa046..c76fbd2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
=edition = "2024"
=
=[dependencies]
+anyhow = "1.0.100"
=clap = { version = "4.5.51", features = ["derive"] }
=colored = "3.0.0"
=env_logger = "0.11.8"index 787b9bc..a187408 100644
--- a/src/report.rs
+++ b/src/report.rs
@@ -1,7 +1,7 @@
=use crate::spec::{Scenario, Spec, Step, Suite};
+use anyhow::anyhow;
=use colored::Colorize;
=use serde::{Deserialize, Serialize};
-use std::error::Error;
=use std::fmt::Display;
=use std::io::{BufRead, BufReader, LineWriter, Write};
=use std::process::{Command, Stdio};
@@ -58,7 +58,7 @@ pub struct ScenarioReport<'a> {
=}
=impl<'a> ScenarioReport<'a> {
= // TODO: The ScenarioReport::run method is very effectful. I think it should live in it's own module.
- fn run(&mut self, interpreter: &str, verbose: bool) -> Result<(), Box<dyn Error>> {
+ fn run(&mut self, interpreter: &str, verbose: bool) -> anyhow::Result<()> {
= log::debug!(
= "Running scenario '{}' using '{interpreter}' as an interpreter",
= self.scenario.title
@@ -77,11 +77,15 @@ impl<'a> ScenarioReport<'a> {
=
= let Some(input) = process.stdin.take() else {
= // TODO: Avoid string errors!
- return Err("Failed to take the stdin stream of the interpreter process.".into());
+ return Err(anyhow!(
+ "Failed to take the stdin stream of the interpreter process."
+ ));
= };
=
= let Some(output) = process.stdout.take() else {
- return Err("Failed to take the stdout stream of the interpreter process.".into());
+ return Err(anyhow!(
+ "Failed to take the stdout stream of the interpreter process."
+ ));
= };
=
= let mut reader = BufReader::new(output);
@@ -91,9 +95,9 @@ impl<'a> ScenarioReport<'a> {
= let ready = match Self::receive(&mut reader)? {
= InterpreterMessage::InterpreterState { ready } => ready,
= message => {
- let complaint =
- format!("Unexpected message received from the interpreter: {message:#?}");
- return Err(complaint.into());
+ return Err(anyhow!(
+ "Unexpected message received from the interpreter: {message:#?}"
+ ));
= }
= };
= if ready {
@@ -134,10 +138,9 @@ impl<'a> ScenarioReport<'a> {
= break;
= }
= unexpected => {
- let message = format!(
+ return Err(anyhow!(
= "Unexpected message received from the interpreter: {unexpected:#?}"
- );
- return Err(message.into());
+ ));
= }
= };
= }
@@ -146,15 +149,14 @@ impl<'a> ScenarioReport<'a> {
=
= process
= .wait()
- .map_err(|io_error| Box::new(io_error).into()) // No idea why it's needed.
+ .map_err(|io_error| { anyhow::Error::from(io_error) })
= .and_then(|exit_status| {
= log::debug!("Interpreter closed with exit status {exit_status:?}");
= let exit_code = exit_status.code().unwrap_or(0); // Is that right?
= if exit_code == 0 {
= Ok(())
= } else {
- let message = format!("Interpreter process {interpreter} exited abnormally. Exit code: {exit_code}");
- Err(message.into())
+ Err(anyhow!("Interpreter process {interpreter} exited abnormally. Exit code: {exit_code}"))
= }
= })
= }
@@ -163,14 +165,14 @@ impl<'a> ScenarioReport<'a> {
= fn send(
= writer: &mut LineWriter<std::process::ChildStdin>,
= message: &ControlMessage,
- ) -> Result<(), Box<dyn Error>> {
+ ) -> anyhow::Result<()> {
= let json = serde_json::to_string(&message)?;
= Self::write_line(writer, &json)
= }
=
= fn receive(
= reader: &mut BufReader<std::process::ChildStdout>,
- ) -> Result<InterpreterMessage, Box<dyn Error>> {
+ ) -> anyhow::Result<InterpreterMessage> {
= let buffer = Self::read_line(reader)?;
= let message = serde_json::from_str(&buffer)?;
=
@@ -180,7 +182,7 @@ impl<'a> ScenarioReport<'a> {
= fn write_line(
= writer: &mut LineWriter<std::process::ChildStdin>,
= line: &str,
- ) -> Result<(), Box<dyn Error>> {
+ ) -> anyhow::Result<()> {
= let buffer = [line, "\n"].concat();
=
= log::debug!("Sending: {}", buffer);
@@ -188,12 +190,10 @@ impl<'a> ScenarioReport<'a> {
= Ok(())
= }
=
- fn read_line(
- reader: &mut BufReader<std::process::ChildStdout>,
- ) -> Result<String, Box<dyn Error>> {
+ fn read_line(reader: &mut BufReader<std::process::ChildStdout>) -> anyhow::Result<String> {
= let mut buffer = String::new();
= if reader.read_line(&mut buffer)? == 0 {
- return Err("Can't read from the interpreter process.".into());
+ return Err(anyhow!("Can't read from the interpreter process."));
= };
= Ok(buffer)
= }
@@ -213,7 +213,7 @@ pub enum StepStatus {
=pub enum ScenarioStatus {
= Done,
= Pending,
- FailedToRun { error: Box<dyn Error> },
+ FailedToRun { error: anyhow::Error },
=}
=
=impl Display for EvaluationReport<'_> {index 4249efd..f58e607 100644
--- a/src/spec.rs
+++ b/src/spec.rs
@@ -1,5 +1,5 @@
+use anyhow::anyhow;
=use serde::{Deserialize, Serialize};
-use std::error::Error;
=use std::fmt::Display;
=
=/// Spec is a collection of suites that together describe a system
@@ -13,7 +13,7 @@ pub struct Spec {
=
=impl Spec {
= /// Load suites from a markdown document
- pub fn load_document(&mut self, md: &str) -> Result<(), Box<dyn Error>> {
+ pub fn load_document(&mut self, md: &str) -> anyhow::Result<()> {
= // TODO: Support loading multiple suits from a single document (demarcated by h1)
= let suite = Suite::from_markdown(md)?;
=
@@ -98,7 +98,7 @@ pub struct FrontMatter {
=}
=
=impl Suite {
- pub fn from_markdown(md: &str) -> Result<Self, Box<dyn Error>> {
+ pub fn from_markdown(md: &str) -> anyhow::Result<Self> {
= let mdast = markdown::to_mdast(
= md,
= &markdown::ParseOptions {
@@ -109,7 +109,9 @@ impl Suite {
= ..Default::default()
= },
= )
- .map_err(|message| message.to_string())?;
+ .map_err(|message| {
+ anyhow!("Failed to parse markdown input").context(message.to_string())
+ })?;
= log::debug!("Markdown parsed:\n\n{:#?}", mdast);
=
= Self::try_from(mdast)
@@ -117,17 +119,29 @@ impl Suite {
=}
=
=impl TryFrom<markdown::mdast::Node> for Suite {
- type Error = Box<dyn Error>;
+ type Error = anyhow::Error;
=
- fn try_from(mdast: markdown::mdast::Node) -> Result<Self, Self::Error> {
+ fn try_from(mdast: markdown::mdast::Node) -> anyhow::Result<Self> {
= // Find the YAML front-matter and extract the interpreter field
- let children = mdast.children().ok_or("The given node has no children.")?;
+ let children = mdast
+ .children()
+ .ok_or(anyhow!("The given node has no children."))?;
= let first = children
= .first()
- .ok_or("There is no first child in the given node.")?;
+ .ok_or(anyhow!("There is no first child in the given node."))?;
=
= let markdown::mdast::Node::Yaml(markdown::mdast::Yaml { value: yaml, .. }) = first else {
- return Err("First child is not a YAML front matter.".into());
+ return Err(
+ (anyhow!("First child is not a YAML front matter.")) .context(concat!(
+ "At the top of your markdown document you need to provide a YAML front-matter that looks something like this:\n",
+ "\n",
+ "---\n",
+ "interpreter: ./my-interpreter.sh --any arguments you like\n",
+ "---\n",
+ "\n",
+ "The rest of the markdown should follow.\n"
+ )),
+ );
= };
= let frontmatter: FrontMatter = serde_yaml::from_str(yaml)?;
=
@@ -145,7 +159,9 @@ impl TryFrom<markdown::mdast::Node> for Suite {
= }
= })
= .next()
- .ok_or("There is no level 1 heading inside the given node.")?
+ .ok_or(anyhow!(
+ "There is no level 1 heading inside the given node."
+ ))?
= .to_string();
=
= // Extract scenarios and steps
@@ -195,19 +211,23 @@ impl TryFrom<markdown::mdast::Node> for Suite {
=}
=
=impl TryFrom<&markdown::mdast::ListItem> for Step {
- type Error = Box<dyn Error>;
+ type Error = anyhow::Error;
=
= fn try_from(item: &markdown::mdast::ListItem) -> Result<Self, Self::Error> {
= let headline = item
= .children
= .first()
- .ok_or("Encountered a list item without any children. Impossible?")?
+ .ok_or(anyhow!(
+ "Encountered a list item without any children. Impossible?"
+ ))?
= .clone();
= let mut arguments = Vec::new();
= let mut variant_headline = headline.clone();
= for child in variant_headline
= .children_mut()
- .ok_or("Encountered a list heading with an empty first child. Impossible?")?
+ .ok_or(anyhow!(
+ "Encountered a list heading with an empty first child. Impossible?"
+ ))?
= .iter_mut()
= {
= if let markdown::mdast::Node::InlineCode(inline_code) = child {Provide more context when failing
Using anyhow context feature.
index 2811a83..177a8fb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,6 +179,15 @@ dependencies = [
= "hashbrown",
=]
=
+[[package]]
+name = "indoc"
+version = "2.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
+dependencies = [
+ "rustversion",
+]
+
=[[package]]
=name = "is_terminal_polyfill"
=version = "1.70.2"
@@ -304,6 +313,12 @@ version = "0.8.8"
=source = "registry+https://github.com/rust-lang/crates.io-index"
=checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
=
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
=[[package]]
=name = "ryu"
=version = "1.0.20"
@@ -392,6 +407,7 @@ dependencies = [
= "colored",
= "env_logger",
= "glob",
+ "indoc",
= "log",
= "markdown",
= "serde",index c76fbd2..7e168bb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,7 @@ clap = { version = "4.5.51", features = ["derive"] }
=colored = "3.0.0"
=env_logger = "0.11.8"
=glob = "0.3.3"
+indoc = "2.0.7"
=log = "0.4.28"
=markdown = "1.0.0"
=serde = { version = "1.0.228", features = ["serde_derive"] }index b5da4fa..1e3dab7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,13 +1,13 @@
=mod report;
=mod spec;
=
+use anyhow::{Context, Error, bail};
=use clap::Parser;
=use env_logger;
=use glob::glob;
=use log;
=use report::EvaluationReport;
=use spec::Spec;
-use std::error::Error;
=use std::path::PathBuf;
=
=#[derive(Parser)]
@@ -22,7 +22,7 @@ struct Cli {
= verbose: bool,
=}
=
-fn main() -> Result<(), Box<dyn Error>> {
+fn main() -> Result<(), Error> {
= let cli = Cli::parse();
= let log_env = env_logger::Env::default()
= .filter_or("RUST_LOG", if cli.verbose { "trace" } else { "info" });
@@ -41,15 +41,20 @@ fn main() -> Result<(), Box<dyn Error>> {
= let pattern = format!("{}/**/*.md", input.display());
=
= for path in glob(&pattern)? {
- let md = std::fs::read_to_string(path?)?;
- spec.load_document(&md)?;
+ let path = path.context(format!("resolving {pattern}"))?;
+ let md = std::fs::read_to_string(&path)
+ .context(format!("reading a file at {}", path.display()))?;
+ spec.load_document(&md)
+ .context(format!("loading a document from {}", path.display()))?;
= }
= } else if input.is_file() {
= log::debug!("The {} is a file. Reading...", input.display());
- let md = std::fs::read_to_string(input)?;
- spec.load_document(&md)?;
+ let md = std::fs::read_to_string(&input)
+ .context(format!("reading a file at {}", input.display()))?;
+ spec.load_document(&md)
+ .context(format!("loading a document from {}", input.display()))?;
= } else {
- return Err(format!("The {} is neither a file nor directory", input.display()).into());
+ bail!("The {} is neither a file nor directory", input.display());
= };
=
= log::info!("Collected {} suites.", spec.suites.len());index a187408..5ccc2b2 100644
--- a/src/report.rs
+++ b/src/report.rs
@@ -1,6 +1,7 @@
=use crate::spec::{Scenario, Spec, Step, Suite};
-use anyhow::anyhow;
+use anyhow::{Context, anyhow};
=use colored::Colorize;
+use indoc::formatdoc;
=use serde::{Deserialize, Serialize};
=use std::fmt::Display;
=use std::io::{BufRead, BufReader, LineWriter, Write};
@@ -41,7 +42,9 @@ impl<'a> SuiteReport<'a> {
= log::debug!("Evaluating suite {}", self.suite.title);
=
= for scenario in self.scenarios.iter_mut() {
- let result = scenario.run(&self.suite.interpreter, verbose);
+ let result = scenario
+ .run(&self.suite.interpreter, verbose)
+ .context(format!("running scenario: {}", scenario.scenario.title));
= if let Err(error) = result {
= scenario.status = ScenarioStatus::FailedToRun { error }
= } else {
@@ -73,18 +76,19 @@ impl<'a> ScenarioReport<'a> {
= "tbb_interpreter_log",
= if verbose { "debug" } else { "info" },
= )
- .spawn()?;
+ .spawn()
+ .context(format!("spawning interpreter from {interpreter}"))?;
=
= let Some(input) = process.stdin.take() else {
= // TODO: Avoid string errors!
= return Err(anyhow!(
- "Failed to take the stdin stream of the interpreter process."
+ "failed to take the stdin stream of the interpreter process"
= ));
= };
=
= let Some(output) = process.stdout.take() else {
= return Err(anyhow!(
- "Failed to take the stdout stream of the interpreter process."
+ "failed to take the stdout stream of the interpreter process"
= ));
= };
=
@@ -92,11 +96,11 @@ impl<'a> ScenarioReport<'a> {
= let mut writer = LineWriter::new(input);
=
= loop {
- let ready = match Self::receive(&mut reader)? {
+ let ready = match Self::receive(&mut reader).context("awaiting for the interpreter")? {
= InterpreterMessage::InterpreterState { ready } => ready,
= message => {
= return Err(anyhow!(
- "Unexpected message received from the interpreter: {message:#?}"
+ "unexpected message received from the interpreter: {message:#?}"
= ));
= }
= };
@@ -139,7 +143,7 @@ impl<'a> ScenarioReport<'a> {
= }
= unexpected => {
= return Err(anyhow!(
- "Unexpected message received from the interpreter: {unexpected:#?}"
+ "unexpected message received from the interpreter: {unexpected:#?}"
= ));
= }
= };
@@ -149,15 +153,20 @@ impl<'a> ScenarioReport<'a> {
=
= process
= .wait()
- .map_err(|io_error| { anyhow::Error::from(io_error) })
+ .map_err(|io_error| anyhow::Error::from(io_error))
= .and_then(|exit_status| {
= log::debug!("Interpreter closed with exit status {exit_status:?}");
= let exit_code = exit_status.code().unwrap_or(0); // Is that right?
= if exit_code == 0 {
= Ok(())
= } else {
- Err(anyhow!("Interpreter process {interpreter} exited abnormally. Exit code: {exit_code}"))
- }
+ Err(anyhow!(formatdoc! {r#"
+ interpreter process exited abnormally
+
+ Interpreter: {interpreter}
+ Exit code: {exit_code}"
+ "#}))
+ }
= })
= }
=
@@ -193,7 +202,7 @@ impl<'a> ScenarioReport<'a> {
= fn read_line(reader: &mut BufReader<std::process::ChildStdout>) -> anyhow::Result<String> {
= let mut buffer = String::new();
= if reader.read_line(&mut buffer)? == 0 {
- return Err(anyhow!("Can't read from the interpreter process."));
+ return Err(anyhow!("can't read from the interpreter process"));
= };
= Ok(buffer)
= }
@@ -239,7 +248,11 @@ impl Display for EvaluationReport<'_> {
= };
= writeln!(f, "\n {sigil} {title}\n", title = scenario.title)?;
= if let ScenarioStatus::FailedToRun { error } = status {
- writeln!(f, " {}\n", error.to_string().red())?;
+ writeln!(f, " {}\n", error.root_cause().to_string().red())?;
+ for cause in error.chain() {
+ writeln!(f, " * {}", cause)?;
+ }
+ writeln!(f, "\n")?;
= }
=
= for StepReport { step, status } in steps.iter() {index f58e607..9da0529 100644
--- a/src/spec.rs
+++ b/src/spec.rs
@@ -1,4 +1,5 @@
-use anyhow::anyhow;
+use anyhow::{Context, anyhow, bail};
+use indoc::indoc;
=use serde::{Deserialize, Serialize};
=use std::fmt::Display;
=
@@ -15,7 +16,7 @@ impl Spec {
= /// Load suites from a markdown document
= pub fn load_document(&mut self, md: &str) -> anyhow::Result<()> {
= // TODO: Support loading multiple suits from a single document (demarcated by h1)
- let suite = Suite::from_markdown(md)?;
+ let suite = Suite::from_markdown(md).context("loading a markdown document")?;
=
= self.suites.push(suite);
= Ok(())
@@ -110,11 +111,11 @@ impl Suite {
= },
= )
= .map_err(|message| {
- anyhow!("Failed to parse markdown input").context(message.to_string())
+ anyhow!("failed to parse markdown input").context(message.to_string())
= })?;
= log::debug!("Markdown parsed:\n\n{:#?}", mdast);
=
- Self::try_from(mdast)
+ Self::try_from(mdast).context("extracting a suite from a markdown document")
= }
=}
=
@@ -123,27 +124,24 @@ impl TryFrom<markdown::mdast::Node> for Suite {
=
= fn try_from(mdast: markdown::mdast::Node) -> anyhow::Result<Self> {
= // Find the YAML front-matter and extract the interpreter field
- let children = mdast
- .children()
- .ok_or(anyhow!("The given node has no children."))?;
- let first = children
- .first()
- .ok_or(anyhow!("There is no first child in the given node."))?;
+ let children = mdast.children().context("the markdown document is empty")?;
+ let first = children.first().context("the markdown document is empty")?;
=
= let markdown::mdast::Node::Yaml(markdown::mdast::Yaml { value: yaml, .. }) = first else {
- return Err(
- (anyhow!("First child is not a YAML front matter.")) .context(concat!(
- "At the top of your markdown document you need to provide a YAML front-matter that looks something like this:\n",
- "\n",
- "---\n",
- "interpreter: ./my-interpreter.sh --any arguments you like\n",
- "---\n",
- "\n",
- "The rest of the markdown should follow.\n"
- )),
- );
+ bail!(indoc! {r#"
+ missing YAML front-matter
+
+ At the top of your markdown document you need to provide a YAML front-matter that looks something like this:
+
+ ---
+ interpreter: ./my-interpreter.sh --any arguments you like
+ ---
+
+ The rest of the markdown should follow.
+ "#});
= };
- let frontmatter: FrontMatter = serde_yaml::from_str(yaml)?;
+ let frontmatter: FrontMatter =
+ serde_yaml::from_str(yaml).context("Failed to parse front-matter as YAML")?;
=
= // Find h1 and use it as title (
= // TODO: make sure there's only one and it's before any h2
@@ -159,9 +157,15 @@ impl TryFrom<markdown::mdast::Node> for Suite {
= }
= })
= .next()
- .ok_or(anyhow!(
- "There is no level 1 heading inside the given node."
- ))?
+ .context(indoc! {r#"
+ no h1 heading found
+
+ Each markdown document must have at least one h1 heading, like this:
+
+ # Some important aspect of the spec
+
+ This will be used as a title of a suite.
+ "#})?
= .to_string();
=
= // Extract scenarios and steps
@@ -217,17 +221,13 @@ impl TryFrom<&markdown::mdast::ListItem> for Step {
= let headline = item
= .children
= .first()
- .ok_or(anyhow!(
- "Encountered a list item without any children. Impossible?"
- ))?
+ .context("a list without children (how is that even possible?)")?
= .clone();
= let mut arguments = Vec::new();
= let mut variant_headline = headline.clone();
= for child in variant_headline
= .children_mut()
- .ok_or(anyhow!(
- "Encountered a list heading with an empty first child. Impossible?"
- ))?
+ .context("a list heading with an empty first child (how is that even possible?)")?
= .iter_mut()
= {
= if let markdown::mdast::Node::InlineCode(inline_code) = child {Bump the patch version
index 177a8fb..3abc786 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -400,7 +400,7 @@ dependencies = [
=
=[[package]]
=name = "tad-better-behavior"
-version = "0.1.0"
+version = "0.1.1"
=dependencies = [
= "anyhow",
= "clap",index 7e168bb..3601fd4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
=[package]
=name = "tad-better-behavior"
-version = "0.1.0"
+version = "0.1.1"
=edition = "2024"
=
=[dependencies]More context for message passing errors
index 5ccc2b2..4eed1ef 100644
--- a/src/report.rs
+++ b/src/report.rs
@@ -175,7 +175,7 @@ impl<'a> ScenarioReport<'a> {
= writer: &mut LineWriter<std::process::ChildStdin>,
= message: &ControlMessage,
= ) -> anyhow::Result<()> {
- let json = serde_json::to_string(&message)?;
+ let json = serde_json::to_string(&message).context(format!("sending data: {message:?}"))?;
= Self::write_line(writer, &json)
= }
=
@@ -183,7 +183,8 @@ impl<'a> ScenarioReport<'a> {
= reader: &mut BufReader<std::process::ChildStdout>,
= ) -> anyhow::Result<InterpreterMessage> {
= let buffer = Self::read_line(reader)?;
- let message = serde_json::from_str(&buffer)?;
+ let message =
+ serde_json::from_str(&buffer).context(format!("received string: {buffer:?}"))?;
=
= Ok(message)
= }
@@ -195,7 +196,9 @@ impl<'a> ScenarioReport<'a> {
= let buffer = [line, "\n"].concat();
=
= log::debug!("Sending: {}", buffer);
- writer.write_all(buffer.as_bytes())?;
+ writer
+ .write_all(buffer.as_bytes())
+ .context(format!("writing data: {buffer:?}"))?;
= Ok(())
= }
=Bump the patch version
index 3abc786..922de52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -400,7 +400,7 @@ dependencies = [
=
=[[package]]
=name = "tad-better-behavior"
-version = "0.1.1"
+version = "0.1.2"
=dependencies = [
= "anyhow",
= "clap",index 3601fd4..b7c5dcd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
=[package]
=name = "tad-better-behavior"
-version = "0.1.1"
+version = "0.1.2"
=edition = "2024"
=
=[dependencies]