diff --git a/Cargo.toml b/Cargo.toml
index a6bf453..0515a7a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
members = [
+ "color-spantrace",
"eyre"
]
diff --git a/color-spantrace/.gitignore b/color-spantrace/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/color-spantrace/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/color-spantrace/CHANGELOG.md b/color-spantrace/CHANGELOG.md
new file mode 100644
index 0000000..3014914
--- /dev/null
+++ b/color-spantrace/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+
+
+## [Unreleased] - ReleaseDate
+
+## [0.2.0] - 2022-01-12
+### Changed
+- Updated dependency versions to match latest tracing versions
+
+## [0.1.6] - 2020-12-02
+### Fixed
+- Ignore all io errors when resolving source files instead of only file not
+ found errors
+
+## [v0.1.5] - 2020-12-01
+### Added
+- Support custom color themes for spantrace format
+
+
+[Unreleased]: https://github.com/eyre-rs/color-spantrace/compare/v0.2.0...HEAD
+[0.2.0]: https://github.com/eyre-rs/color-spantrace/compare/v0.1.6...v0.2.0
+[0.1.6]: https://github.com/eyre-rs/color-spantrace/compare/v0.1.5...v0.1.6
+[v0.1.5]: https://github.com/eyre-rs/color-spantrace/releases/tag/v0.1.5
diff --git a/color-spantrace/Cargo.toml b/color-spantrace/Cargo.toml
new file mode 100644
index 0000000..e1510d8
--- /dev/null
+++ b/color-spantrace/Cargo.toml
@@ -0,0 +1,64 @@
+[package]
+name = "color-spantrace"
+version = "0.2.0"
+description = "A pretty printer for tracing_error::SpanTrace based on color-backtrace"
+documentation = "https://docs.rs/color-spantrace"
+
+authors = { workspace = true }
+edition = { workspace = true }
+license = { workspace = true }
+repository = { workspace = true }
+readme = { workspace = true }
+rust-version = { workspace = true }
+
+[dependencies]
+tracing-error = "0.2.0"
+tracing-core = "0.1.21"
+owo-colors = "3.2.0"
+once_cell = { workspace = true }
+
+[dev-dependencies]
+tracing-subscriber = "0.3.4"
+tracing = "0.1.29"
+ansi-parser = "0.8" # used for testing color schemes
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
+
+[package.metadata.release]
+dev-version = false
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "Unreleased"
+replace="{{version}}"
+
+[[package.metadata.release.pre-release-replacements]]
+file = "src/lib.rs"
+search = "#!\\[doc\\(html_root_url.*"
+replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]"
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "\\.\\.\\.HEAD"
+replace="...{{tag_name}}"
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "ReleaseDate"
+replace="{{date}}"
+
+[[package.metadata.release.pre-release-replacements]]
+file="CHANGELOG.md"
+search=""
+replace="\n\n## [Unreleased] - ReleaseDate"
+exactly=1
+
+[[package.metadata.release.pre-release-replacements]]
+file="CHANGELOG.md"
+search=""
+replace="\n[Unreleased]: https://github.com/eyre-rs/{{crate_name}}/compare/{{tag_name}}...HEAD"
+exactly=1
diff --git a/color-spantrace/LICENSE-APACHE b/color-spantrace/LICENSE-APACHE
new file mode 120000
index 0000000..965b606
--- /dev/null
+++ b/color-spantrace/LICENSE-APACHE
@@ -0,0 +1 @@
+../LICENSE-APACHE
\ No newline at end of file
diff --git a/color-spantrace/LICENSE-MIT b/color-spantrace/LICENSE-MIT
new file mode 120000
index 0000000..76219eb
--- /dev/null
+++ b/color-spantrace/LICENSE-MIT
@@ -0,0 +1 @@
+../LICENSE-MIT
\ No newline at end of file
diff --git a/color-spantrace/README.md b/color-spantrace/README.md
new file mode 100644
index 0000000..a2eaec9
--- /dev/null
+++ b/color-spantrace/README.md
@@ -0,0 +1,110 @@
+color-spantrace
+===============
+
+[![Build Status][actions-badge]][actions-url]
+[![Latest Version](https://img.shields.io/crates/v/color-spantrace.svg)](https://crates.io/crates/color-spantrace)
+[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/color-spantrace)
+
+[actions-badge]: https://github.com/eyre-rs/color-spantrace/workflows/Continuous%20integration/badge.svg
+[actions-url]: https://github.com/eyre-rs/color-spantrace/actions?query=workflow%3A%22Continuous+integration%22
+
+A rust library for colorizing [`tracing_error::SpanTrace`] objects in the style
+of [`color-backtrace`].
+
+## Setup
+
+Add the following to your `Cargo.toml`:
+
+```toml
+[dependencies]
+color-spantrace = "0.2"
+tracing = "0.1"
+tracing-error = "0.2"
+tracing-subscriber = "0.3"
+```
+
+Setup a tracing subscriber with an `ErrorLayer`:
+
+```rust
+use tracing_error::ErrorLayer;
+use tracing_subscriber::{prelude::*, registry::Registry};
+
+Registry::default().with(ErrorLayer::default()).init();
+```
+
+Create spans and enter them:
+
+```rust
+use tracing::instrument;
+use tracing_error::SpanTrace;
+
+#[instrument]
+fn foo() -> SpanTrace {
+ SpanTrace::capture()
+}
+```
+
+And finally colorize the `SpanTrace`:
+
+```rust
+use tracing_error::SpanTrace;
+
+let span_trace = SpanTrace::capture();
+println!("{}", color_spantrace::colorize(&span_trace));
+```
+
+## Example
+
+This example is taken from `examples/usage.rs`:
+
+```rust
+use tracing::instrument;
+use tracing_error::{ErrorLayer, SpanTrace};
+use tracing_subscriber::{prelude::*, registry::Registry};
+
+#[instrument]
+fn main() {
+ Registry::default().with(ErrorLayer::default()).init();
+
+ let span_trace = one(42);
+ println!("{}", color_spantrace::colorize(&span_trace));
+}
+
+#[instrument]
+fn one(i: u32) -> SpanTrace {
+ two()
+}
+
+#[instrument]
+fn two() -> SpanTrace {
+ SpanTrace::capture()
+}
+```
+
+This creates the following output
+
+### Minimal Format
+
+![minimal format](./pictures/minimal.png)
+
+### Full Format
+
+![Full format](./pictures/full.png)
+
+#### License
+
+
+Licensed under either of Apache License, Version
+2.0 or MIT license at your option.
+
+
+
+
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+
+
+[`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html
+[`color-backtrace`]: https://github.com/athre0z/color-backtrace
diff --git a/color-spantrace/examples/usage.rs b/color-spantrace/examples/usage.rs
new file mode 100644
index 0000000..21cca48
--- /dev/null
+++ b/color-spantrace/examples/usage.rs
@@ -0,0 +1,21 @@
+use tracing::instrument;
+use tracing_error::{ErrorLayer, SpanTrace};
+use tracing_subscriber::{prelude::*, registry::Registry};
+
+#[instrument]
+fn main() {
+ Registry::default().with(ErrorLayer::default()).init();
+
+ let span_trace = one(42);
+ println!("{}", color_spantrace::colorize(&span_trace));
+}
+
+#[instrument]
+fn one(i: u32) -> SpanTrace {
+ two()
+}
+
+#[instrument]
+fn two() -> SpanTrace {
+ SpanTrace::capture()
+}
diff --git a/color-spantrace/pictures/full.png b/color-spantrace/pictures/full.png
new file mode 100644
index 0000000..489da11
Binary files /dev/null and b/color-spantrace/pictures/full.png differ
diff --git a/color-spantrace/pictures/minimal.png b/color-spantrace/pictures/minimal.png
new file mode 100644
index 0000000..7d3dfd6
Binary files /dev/null and b/color-spantrace/pictures/minimal.png differ
diff --git a/color-spantrace/src/lib.rs b/color-spantrace/src/lib.rs
new file mode 100644
index 0000000..7d51313
--- /dev/null
+++ b/color-spantrace/src/lib.rs
@@ -0,0 +1,376 @@
+//! A rust library for colorizing [`tracing_error::SpanTrace`] objects in the style
+//! of [`color-backtrace`].
+//!
+//! ## Setup
+//!
+//! Add the following to your `Cargo.toml`:
+//!
+//! ```toml
+//! [dependencies]
+//! color-spantrace = "0.2"
+//! tracing = "0.1"
+//! tracing-error = "0.2"
+//! tracing-subscriber = "0.3"
+//! ```
+//!
+//! Setup a tracing subscriber with an `ErrorLayer`:
+//!
+//! ```rust
+//! use tracing_error::ErrorLayer;
+//! use tracing_subscriber::{prelude::*, registry::Registry};
+//!
+//! Registry::default().with(ErrorLayer::default()).init();
+//! ```
+//!
+//! Create spans and enter them:
+//!
+//! ```rust
+//! use tracing::instrument;
+//! use tracing_error::SpanTrace;
+//!
+//! #[instrument]
+//! fn foo() -> SpanTrace {
+//! SpanTrace::capture()
+//! }
+//! ```
+//!
+//! And finally colorize the `SpanTrace`:
+//!
+//! ```rust
+//! use tracing_error::SpanTrace;
+//!
+//! let span_trace = SpanTrace::capture();
+//! println!("{}", color_spantrace::colorize(&span_trace));
+//! ```
+//!
+//! ## Output Format
+//!
+//! Running `examples/usage.rs` from the `color-spantrace` repo produces the following output:
+//!
+//!
❯ cargo run --example usage
+//! Finished dev [unoptimized + debuginfo] target(s) in 0.04s
+//! Running `target/debug/examples/usage`
+//! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+//!
+//! 0: usage::two
+//! at examples/usage.rs:18
+//! 1: usage::one with i=42
+//! at examples/usage.rs:13
+//!
+//! [`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html
+//! [`color-backtrace`]: https://github.com/athre0z/color-backtrace
+#![doc(html_root_url = "https://docs.rs/color-spantrace/0.2.0")]
+#![warn(
+ missing_debug_implementations,
+ missing_docs,
+ rustdoc::missing_doc_code_examples,
+ rust_2018_idioms,
+ unreachable_pub,
+ bad_style,
+ dead_code,
+ improper_ctypes,
+ non_shorthand_field_patterns,
+ no_mangle_generic_items,
+ overflowing_literals,
+ path_statements,
+ patterns_in_fns_without_body,
+ private_in_public,
+ unconditional_recursion,
+ unused,
+ unused_allocation,
+ unused_comparisons,
+ unused_parens,
+ while_true
+)]
+use once_cell::sync::OnceCell;
+use owo_colors::{style, Style};
+use std::env;
+use std::fmt;
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+use tracing_error::SpanTrace;
+
+static THEME: OnceCell = OnceCell::new();
+
+/// A struct that represents theme that is used by `color_spantrace`
+#[derive(Debug, Copy, Clone, Default)]
+pub struct Theme {
+ file: Style,
+ line_number: Style,
+ target: Style,
+ fields: Style,
+ active_line: Style,
+}
+
+impl Theme {
+ /// Create blank theme
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// A theme for a dark background. This is the default
+ pub fn dark() -> Self {
+ Self {
+ file: style().purple(),
+ line_number: style().purple(),
+ active_line: style().white().bold(),
+ target: style().bright_red(),
+ fields: style().bright_cyan(),
+ }
+ }
+
+ // XXX same as with `light` in `color_eyre`
+ /// A theme for a light background
+ pub fn light() -> Self {
+ Self {
+ file: style().purple(),
+ line_number: style().purple(),
+ target: style().red(),
+ fields: style().blue(),
+ active_line: style().bold(),
+ }
+ }
+
+ /// Styles printed paths
+ pub fn file(mut self, style: Style) -> Self {
+ self.file = style;
+ self
+ }
+
+ /// Styles the line number of a file
+ pub fn line_number(mut self, style: Style) -> Self {
+ self.line_number = style;
+ self
+ }
+
+ /// Styles the target (i.e. the module and function name, and so on)
+ pub fn target(mut self, style: Style) -> Self {
+ self.target = style;
+ self
+ }
+
+ /// Styles fields associated with a the `tracing::Span`.
+ pub fn fields(mut self, style: Style) -> Self {
+ self.fields = style;
+ self
+ }
+
+ /// Styles the selected line of displayed code
+ pub fn active_line(mut self, style: Style) -> Self {
+ self.active_line = style;
+ self
+ }
+}
+
+/// An error returned by `set_theme` if a global theme was already set
+#[derive(Debug)]
+pub struct InstallThemeError;
+
+impl fmt::Display for InstallThemeError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("could not set the provided `Theme` globally as another was already set")
+ }
+}
+
+impl std::error::Error for InstallThemeError {}
+
+/// Sets the global theme.
+///
+/// # Details
+///
+/// This can only be set once and otherwise fails.
+///
+/// **Note:** `colorize` sets the global theme implicitly, if it was not set already. So calling `colorize` and then `set_theme` fails
+pub fn set_theme(theme: Theme) -> Result<(), InstallThemeError> {
+ THEME.set(theme).map_err(|_| InstallThemeError)
+}
+
+/// Display a [`SpanTrace`] with colors and source
+///
+/// This function returns an `impl Display` type which can be then used in place of the original
+/// SpanTrace when writing it too the screen or buffer.
+///
+/// # Example
+///
+/// ```rust
+/// use tracing_error::SpanTrace;
+///
+/// let span_trace = SpanTrace::capture();
+/// println!("{}", color_spantrace::colorize(&span_trace));
+/// ```
+///
+/// **Note:** `colorize` sets the global theme implicitly, if it was not set already. So calling `colorize` and then `set_theme` fails
+///
+/// [`SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html
+pub fn colorize(span_trace: &SpanTrace) -> impl fmt::Display + '_ {
+ let theme = *THEME.get_or_init(Theme::dark);
+ ColorSpanTrace { span_trace, theme }
+}
+
+struct ColorSpanTrace<'a> {
+ span_trace: &'a SpanTrace,
+ theme: Theme,
+}
+
+macro_rules! try_bool {
+ ($e:expr, $dest:ident) => {{
+ let ret = $e.unwrap_or_else(|e| $dest = Err(e));
+
+ if $dest.is_err() {
+ return false;
+ }
+
+ ret
+ }};
+}
+
+struct Frame<'a> {
+ metadata: &'a tracing_core::Metadata<'static>,
+ fields: &'a str,
+ theme: Theme,
+}
+
+/// Defines how verbose the backtrace is supposed to be.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum Verbosity {
+ /// Print a small message including the panic payload and the panic location.
+ Minimal,
+ /// Everything in `Minimal` and additionally print a backtrace.
+ Medium,
+ /// Everything in `Medium` plus source snippets for all backtrace locations.
+ Full,
+}
+
+impl Verbosity {
+ fn lib_from_env() -> Self {
+ Self::convert_env(
+ env::var("RUST_LIB_BACKTRACE")
+ .or_else(|_| env::var("RUST_BACKTRACE"))
+ .ok(),
+ )
+ }
+
+ fn convert_env(env: Option) -> Self {
+ match env {
+ Some(ref x) if x == "full" => Verbosity::Full,
+ Some(_) => Verbosity::Medium,
+ None => Verbosity::Minimal,
+ }
+ }
+}
+
+impl Frame<'_> {
+ fn print(&self, i: u32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.print_header(i, f)?;
+ self.print_fields(f)?;
+ self.print_source_location(f)?;
+ Ok(())
+ }
+
+ fn print_header(&self, i: u32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{:>2}: {}{}{}",
+ i,
+ self.theme.target.style(self.metadata.target()),
+ self.theme.target.style("::"),
+ self.theme.target.style(self.metadata.name()),
+ )
+ }
+
+ fn print_fields(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if !self.fields.is_empty() {
+ write!(f, " with {}", self.theme.fields.style(self.fields))?;
+ }
+
+ Ok(())
+ }
+
+ fn print_source_location(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(file) = self.metadata.file() {
+ let lineno = self
+ .metadata
+ .line()
+ .map_or("".to_owned(), |x| x.to_string());
+ write!(
+ f,
+ "\n at {}:{}",
+ self.theme.file.style(file),
+ self.theme.line_number.style(lineno),
+ )?;
+ } else {
+ write!(f, "\n at ")?;
+ }
+
+ Ok(())
+ }
+
+ fn print_source_if_avail(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let (lineno, filename) = match (self.metadata.line(), self.metadata.file()) {
+ (Some(a), Some(b)) => (a, b),
+ // Without a line number and file name, we can't sensibly proceed.
+ _ => return Ok(()),
+ };
+
+ let file = match File::open(filename) {
+ Ok(file) => file,
+ // ignore io errors and just don't print the source
+ Err(_) => return Ok(()),
+ };
+
+ use std::fmt::Write;
+
+ // Extract relevant lines.
+ let reader = BufReader::new(file);
+ let start_line = lineno - 2.min(lineno - 1);
+ let surrounding_src = reader.lines().skip(start_line as usize - 1).take(5);
+ let mut buf = String::new();
+ for (line, cur_line_no) in surrounding_src.zip(start_line..) {
+ if cur_line_no == lineno {
+ write!(
+ &mut buf,
+ "{:>8} > {}",
+ cur_line_no.to_string(),
+ line.unwrap()
+ )?;
+ write!(f, "\n{}", self.theme.active_line.style(&buf))?;
+ buf.clear();
+ } else {
+ write!(f, "\n{:>8} │ {}", cur_line_no, line.unwrap())?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl fmt::Display for ColorSpanTrace<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut err = Ok(());
+ let mut span = 0;
+
+ writeln!(f, "{:━^80}\n", " SPANTRACE ")?;
+ self.span_trace.with_spans(|metadata, fields| {
+ let frame = Frame {
+ metadata,
+ fields,
+ theme: self.theme,
+ };
+
+ if span > 0 {
+ try_bool!(write!(f, "\n",), err);
+ }
+
+ try_bool!(frame.print(span, f), err);
+
+ if Verbosity::lib_from_env() == Verbosity::Full {
+ try_bool!(frame.print_source_if_avail(f), err);
+ }
+
+ span += 1;
+ true
+ });
+
+ err
+ }
+}
diff --git a/color-spantrace/tests/data/theme_control.txt b/color-spantrace/tests/data/theme_control.txt
new file mode 100644
index 0000000..96d8830
--- /dev/null
+++ b/color-spantrace/tests/data/theme_control.txt
@@ -0,0 +1,9 @@
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ 0: [91mthemes[0m[91m::[0m[91mtest_capture[0m with [96mx=42[0m
+ at [35mtests/themes.rs[0m:[35m42[0m
+ 40 │ use tracing_subscriber::{prelude::*, registry::Registry};
+ 41 │
+[37;1m 42 > #[instrument][0m
+ 43 │ fn test_capture(x: u8) -> SpanTrace {
+ 44 │ #[allow(clippy::if_same_then_else)]
\ No newline at end of file
diff --git a/color-spantrace/tests/themes.rs b/color-spantrace/tests/themes.rs
new file mode 100644
index 0000000..2f24d63
--- /dev/null
+++ b/color-spantrace/tests/themes.rs
@@ -0,0 +1,104 @@
+/*
+
+ # How this test works:
+
+ 1) generate a spantrace with `test_capture`
+
+ 2) convert the spantrace to a string
+
+ 3) load stored spantrace control to compare to spantrace string (stored in the path of `control_file_path` below)
+
+ 4) if `control_file_path` doesn't exist, generate corresponding file in the current working directory and request the user to fix the issue (see below)
+
+ 5) extract ANSI escaping sequences (of control and current spantrace)
+
+ 6) compare if the current spantrace and the control contains the same ANSI escape sequences
+
+ 7) If not, fail and show the full strings of the control and the current spantrace
+
+ # Re-generating the control
+
+ If the control spantrace is lost and/or it needs to be re-generated, do the following:
+
+ 1) Checkout the `color_spantrace` version from Git that you want to test against
+
+ 3) Add this test file to '/tests'
+
+ 4) If `control_file_path` exist, delete it
+
+ 5) If you now run this test, it will generate a test control file in the current working directory
+
+ 6) copy this file to `control_file_path` (see instructions that are shown)
+
+*/
+
+use tracing::instrument;
+use tracing_error::SpanTrace;
+
+#[instrument]
+fn test_capture(x: u8) -> SpanTrace {
+ #[allow(clippy::if_same_then_else)]
+ if x == 42 {
+ SpanTrace::capture()
+ } else {
+ SpanTrace::capture()
+ }
+}
+
+#[cfg(not(miri))]
+#[test]
+fn test_backwards_compatibility() {
+ use ansi_parser::{AnsiParser, AnsiSequence, Output};
+ use std::{fs, path::Path};
+ use tracing_error::ErrorLayer;
+ use tracing_subscriber::{prelude::*, registry::Registry};
+ std::env::set_var("RUST_LIB_BACKTRACE", "full");
+
+ // This integration is ran by cargo with cwd="color-spantrace", but the string literals for
+ // `file!` are relative to the workspace root. This changes the cwd to the workspace root.
+ //
+ // The behavior of file! when invoked from a workspace is not documented. See: .
+ //
+ // Noteworthy: non-member path dependencies will get an absolute path, as will registry and git
+ // dependencies.
+ std::env::set_current_dir("..").unwrap();
+
+ Registry::default().with(ErrorLayer::default()).init();
+
+ let spantrace = test_capture(42);
+ let colored_spantrace = format!("{}", color_spantrace::colorize(&spantrace));
+
+ let control_file_name = "theme_control.txt";
+ let control_file_path = ["color-spantrace/tests/data/", control_file_name].concat();
+
+ // If `control_file_path` is missing, save corresponding file to current working directory, and panic with the request to move these files to `control_file_path`, and to commit them to Git. Being explicit (instead of saving directly to `control_file_path` to make sure `control_file_path` is committed to Git. These files anyway should never be missing.
+
+ if !Path::new(&control_file_path).is_file() {
+ std::fs::write(control_file_name, &colored_spantrace)
+ .expect("\n\nError saving `colored_spantrace` to a file");
+ panic!("Required test data missing! Fix this, by moving '{}' to '{}', and commit it to Git.\n\nNote: '{0}' was just generated in the current working directory.\n\n", control_file_name, control_file_path);
+ }
+
+ // `unwrap` should never fail with files generated by this test
+ let colored_spantrace_control =
+ String::from_utf8(fs::read(control_file_path).unwrap()).unwrap();
+
+ fn get_ansi(s: &str) -> impl Iterator- + '_ {
+ s.ansi_parse().filter_map(|x| {
+ if let Output::Escape(ansi) = x {
+ Some(ansi)
+ } else {
+ None
+ }
+ })
+ }
+
+ let colored_spantrace_ansi = get_ansi(&colored_spantrace);
+ let colored_spantrace_control_ansi = get_ansi(&colored_spantrace_control);
+
+ assert!(
+ colored_spantrace_ansi.eq(colored_spantrace_control_ansi),
+ "\x1b[0mANSI escape sequences are not identical to control!\n\nCONTROL:\n\n{}\n\n\n\n{:?}\n\nCURRENT:\n\n{}\n\n\n\n{:?}\n\n", &colored_spantrace_control, &colored_spantrace_control, &colored_spantrace, &colored_spantrace
+ // `\x1b[0m` clears previous ANSI escape sequences
+ );
+}