Skip to main content

cargo_dist/
errors.rs

1//! Errors!
2//!
3//! This module is kind of pointless and stubbed out right now,
4//! because the crate is currently opting for a "typeless" approach
5//! (where everything gets folded into miette::Report right away).
6//!
7//! If we ever change this decision, this will be a lot more important!
8
9use axoproject::errors::AxoprojectError;
10use backtrace::Backtrace;
11use camino::Utf8PathBuf;
12use cargo_dist_schema::{target_lexicon::Triple, ArtifactId, TripleName};
13use color_backtrace::BacktracePrinter;
14use console::style;
15use miette::{Diagnostic, SourceOffset, SourceSpan};
16use thiserror::Error;
17
18/// An alias for the common Result type for this crate
19pub type DistResult<T> = std::result::Result<T, DistError>;
20
21/// Errors dist can have
22#[derive(Debug, Error, Diagnostic)]
23pub enum DistError {
24    /// random i/o error
25    #[error(transparent)]
26    Io(#[from] std::io::Error),
27
28    /// random axoasset error
29    #[error(transparent)]
30    #[diagnostic(transparent)]
31    Asset(#[from] axoasset::AxoassetError),
32
33    /// random axoprocess error
34    #[error(transparent)]
35    #[diagnostic(transparent)]
36    Cmd(#[from] axoprocess::AxoprocessError),
37
38    /// random axoproject error
39    #[error(transparent)]
40    #[diagnostic(transparent)]
41    Project(#[from] axoproject::errors::ProjectError),
42
43    /// random string error
44    #[error(transparent)]
45    FromUtf8Error(#[from] std::string::FromUtf8Error),
46
47    /// random i/o error
48    #[error(transparent)]
49    Goblin(#[from] goblin::error::Error),
50
51    /// random camino conversion error
52    #[error(transparent)]
53    FromPathBufError(#[from] camino::FromPathBufError),
54
55    /// random dialoguer error
56    #[error(transparent)]
57    DialoguerError(#[from] dialoguer::Error),
58
59    /// random axotag error
60    #[error(transparent)]
61    #[diagnostic(transparent)]
62    AxotagError(#[from] axotag::errors::TagError),
63
64    /// random parseint error
65    #[error(transparent)]
66    ParseIntError(#[from] std::num::ParseIntError),
67
68    /// random triple parse error
69    #[error(transparent)]
70    TripleError(#[from] cargo_dist_schema::target_lexicon::ParseError),
71
72    /// A problem with a jinja template, which is always a dist bug
73    #[error("Failed to render template")]
74    #[diagnostic(
75        help("this is a bug in dist, let us know and we'll fix it: https://github.com/axodotdev/cargo-dist/issues/new")
76    )]
77    Jinja {
78        /// The SourceFile we were try to parse
79        #[source_code]
80        source: String,
81        /// The range the error was found on
82        #[label]
83        span: Option<miette::SourceSpan>,
84        /// Details of the error
85        #[source]
86        backtrace: JinjaErrorWithBacktrace,
87    },
88
89    /// Error from (cargo-)wix
90    #[error("WiX returned an error while building {msi}")]
91    Wix {
92        /// The msi we were trying to build
93        msi: String,
94        /// The underlying wix error
95        #[source]
96        details: wix::Error,
97    },
98
99    /// Error from (cargo-)wix init
100    #[error("Couldn't generate main.wxs for {package}'s msi installer")]
101    WixInit {
102        /// The package
103        package: String,
104        /// The underlying wix error
105        #[source]
106        details: wix::Error,
107    },
108
109    /// Error parsing metadata in Cargo.toml (json because it's from cargo-metadata)
110    #[error("Malformed metadata.dist in\n{manifest_path}")]
111    #[diagnostic(help("you can find a reference for the configuration schema at https://axodotdev.github.io/cargo-dist/book/reference/config.html"))]
112    CargoTomlParse {
113        /// path to file
114        manifest_path: Utf8PathBuf,
115        /// Inner error
116        #[source]
117        cause: serde_json::Error,
118    },
119
120    /// User declined to update dist, refuse to make progress
121    #[error("to update your dist config you must use the version your project is configured for")]
122    #[diagnostic(help(
123        "you're running {running_version} but the project is configured for {project_version}"
124    ))]
125    NoUpdateVersion {
126        /// Version the config had
127        project_version: semver::Version,
128        /// Version they're running
129        running_version: semver::Version,
130    },
131
132    /// User tried to enable Github CI support but had inconsistent urls for the repo
133    #[error("Github CI support requires your crates to agree on the URL of your repository")]
134    CantEnableGithubUrlInconsistent {
135        /// inner error that caught this
136        #[diagnostic_source]
137        inner: AxoprojectError,
138    },
139
140    /// User tried to enable Github CI support but no url for the repo
141    #[error("Github CI support requires you to specify the URL of your repository")]
142    #[diagnostic(help(
143        r#"Set the repository = "https://github.com/..." key in these manifests: {manifest_list}"#
144    ))]
145    CantEnableGithubNoUrl {
146        /// List of manifests to change
147        manifest_list: String,
148    },
149
150    /// We got a repository URL but couldn't interpret it as a GitHub repo
151    #[error("GitHub CI support requires a GitHub repository")]
152    CantEnableGithubUrlNotGithub {
153        /// inner error that caught this
154        #[diagnostic_source]
155        inner: AxoprojectError,
156    },
157
158    /// User supplied an illegal npm scope
159    #[error("The npm-scope field must be an all-lowercase value; the supplied value was {scope}")]
160    ScopeMustBeLowercase {
161        /// The incorrectly-formatted scope
162        scope: String,
163    },
164
165    /// Completely unknown format to install-path
166    ///
167    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
168    /// a serde_json error, reducing it to a String. So we inline the help!
169    #[error(r#"install-path = "{path}" has an unknown format (it can either be "CARGO_HOME", "~/subdir/", or "$ENV_VAR/subdir/")"#)]
170    InstallPathInvalid {
171        /// The full value passed to install-path
172        path: String,
173    },
174
175    /// Being pedantic about the env-var mode of install-path to be consistent
176    ///
177    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
178    /// a serde_json error, reducing it to a String. So we inline the help!
179    #[error(r#"install-path = "{path}" is missing a subdirectory (add a trailing slash if you want no subdirectory)"#)]
180    InstallPathEnvSlash {
181        /// The full value passed to install-path
182        path: String,
183    },
184
185    /// Being pedantic about the home mode of install-path to be consistent
186    ///
187    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
188    /// a serde_json error, reducing it to a String. So we inline the help!
189    #[error(r#"install-path = "{path}" is missing a subdirectory (installing directly to home isn't allowed)"#)]
190    InstallPathHomeSubdir {
191        /// The full value passed to install-path
192        path: String,
193    },
194
195    /// explicitly requested workspace builds, but had packages with custom feature settings
196    #[error("precise-builds = false was set, but some packages have custom build features, making it impossible")]
197    #[diagnostic(help("these packages customized either features, no-default-features, or all-features:\n{packages:#?}"))]
198    PreciseImpossible {
199        /// paths of problem manifests
200        packages: Vec<camino::Utf8PathBuf>,
201    },
202
203    /// packages disagreed on homebrew taps
204    #[error("different homebrew taps were set in your workspace, this is currently unsupported")]
205    #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
206    MismatchedTaps {
207        /// paths of problem manifests
208        packages: Vec<camino::Utf8PathBuf>,
209    },
210
211    /// packages disagreed on publishers
212    #[error("different publisher settings were in your workspace, this is currently unuspported")]
213    #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
214    MismatchedPublishers {
215        /// paths of problem manifests
216        packages: Vec<camino::Utf8PathBuf>,
217    },
218
219    /// publishers disagreed on prereleases
220    #[error("different publisher 'prereleases' settings were in your workspace, this is currently unsupported")]
221    MismatchedPrereleases,
222
223    /// parse_tag concluded there was nothing to release
224    #[error("This workspace doesn't have anything for dist to Release!")]
225    NothingToRelease {
226        /// full help printout (very dynamic)
227        #[help]
228        help: String,
229    },
230
231    /// parse_tag concluded there are too many unrelated things for a single tag
232    #[error("There are too many unrelated apps in your workspace to coherently Announce!")]
233    TooManyUnrelatedApps {
234        /// full help printout (very dynamic)
235        #[help]
236        help: String,
237    },
238
239    /// Not an error; indicates that a file's contents differ via --check
240    #[error("{} has out of date contents and needs to be regenerated:\n{diff}", file.origin_path())]
241    #[diagnostic(help("run 'dist init' to update the file\n('allow-dirty' in Cargo.toml to ignore out of date contents)"))]
242    CheckFileMismatch {
243        /// The file whose contents differ
244        file: axoasset::SourceFile,
245        /// The diff
246        diff: String,
247    },
248
249    /// `dist generate` was passed an explicit GenerateMode but the config in their Cargo.toml
250    /// has that mode set to allow-dirty, a contradiction!
251    #[error("'{generate_mode}' is marked as allow-dirty in your dist config, refusing to run")]
252    ContradictoryGenerateModes {
253        /// The problematic mode
254        generate_mode: crate::config::GenerateMode,
255    },
256
257    /// msi/pkg with too many packages
258    #[error("{artifact_name} depends on multiple packages, which isn't yet supported")]
259    #[diagnostic(help("depends on {spec1} and {spec2}"))]
260    MultiPackage {
261        /// Name of the artifact
262        artifact_name: ArtifactId,
263        /// One of the packages
264        spec1: String,
265        /// A different package
266        spec2: String,
267    },
268
269    /// msi/pkg with too few packages
270    #[error("{artifact_name} has no binaries")]
271    #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
272    NoPackage {
273        /// Name of the msi
274        artifact_name: ArtifactId,
275    },
276
277    /// These GUIDs for msi's are required and enforced by `dist generate --check`
278    #[error("missing WiX GUIDs in {manifest_path}: {keys:?}")]
279    #[diagnostic(help("run 'dist init' to generate them"))]
280    MissingWixGuids {
281        /// The Cargo.toml missing them
282        manifest_path: Utf8PathBuf,
283        /// The missing keys
284        keys: &'static [&'static str],
285    },
286
287    /// unrecognized job style
288    #[error("{style} is not a recognized job value")]
289    #[diagnostic(help("Jobs that do not come with dist should be prefixed with ./"))]
290    UnrecognizedJobStyle {
291        /// value provided
292        style: String,
293    },
294
295    /// unrecognized hosting style
296    #[error("{style} is not a recognized release host")]
297    UnrecognizedHostingStyle {
298        /// value provided
299        style: String,
300    },
301
302    /// unrecognized hosting style
303    #[error("No GitHub hosting is defined!")]
304    #[diagnostic(help("Releases must have at least GitHub hosting for updates to be supported."))]
305    NoGitHubHosting {},
306
307    /// unrecognized ci style
308    #[error("{style} is not a recognized ci provider")]
309    UnrecognizedCiStyle {
310        /// value provided
311        style: String,
312    },
313
314    /// unrecognized library style
315    #[error("{style} is not a recognized type of library")]
316    UnrecognizedLibraryStyle {
317        /// value provided
318        style: String,
319    },
320
321    /// Linkage report can't be run for this combination of OS and target
322    #[error("unable to run linkage report for {target} on {host}")]
323    LinkageCheckInvalidOS {
324        /// The OS the check was run on
325        host: String,
326        /// The OS being checked
327        target: TripleName,
328    },
329
330    /// Linkage report can't be run for this target
331    #[error("unable to run linkage report for this type of binary")]
332    LinkageCheckUnsupportedBinary,
333
334    /// Error parsing a string containing an environment variable
335    /// in VAR=value syntax
336    #[error("Unable to parse environment variable as a key/value pair: {line}")]
337    #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
338    EnvParseError {
339        /// The line of text that couldn't be parsed
340        line: String,
341    },
342
343    /// An error running `git archive`
344    #[error("We failed to generate a source tarball for your project")]
345    #[diagnostic(help("This is probably not your fault, please file an issue!"))]
346    GitArchiveError {},
347
348    /// An error running `git -C path rev-parse HEAD`
349    #[error("We failed to query information about the git submodule at\n{path}")]
350    #[diagnostic(help("Does a submodule exist at that path? Has it been fetched with `git submodule update --init`?"))]
351    GitSubmoduleCommitError {
352        /// The path we failed to fetch
353        path: String,
354    },
355
356    /// A required tool is missing
357    #[error("{tool}, required to run this task, is missing")]
358    #[diagnostic(help("Ensure {tool} is installed"))]
359    ToolMissing {
360        /// the name of the missing tool
361        tool: String,
362    },
363
364    /// One or more required tools are missing.
365    #[error("The following tools are required to run this task, but are missing:\n- {}", tools.join("\n- "))]
366    #[diagnostic(help("Please install the tools mentioned above and try again."))]
367    EnvToolsMissing {
368        /// the names of the missing tools
369        tools: Vec<String>,
370    },
371
372    /// Unknown target requested
373    #[error(
374        "A build was requested for {target}, but the standalone updater isn't available for it."
375    )]
376    #[diagnostic(help("At the moment, we can only provide updater binaries for the core set of most common target triples. Please set `install-updater = false` in your config."))]
377    NoAxoupdaterForTarget {
378        /// The target triple being built for
379        target: String,
380    },
381
382    /// reqwest returned non-2xx/404 when checking axoupdater releases
383    #[error("Failed to check the latest release of axoupdater")]
384    #[diagnostic(help(
385        "Is your internet connection working? If not, this may be a bug; please file an issue!"
386    ))]
387    AxoupdaterReleaseCheckFailed {},
388
389    /// Failed to determine how to uncompress something
390    #[error("Failed to determine compression format")]
391    #[diagnostic(help("File extension of unrecognized file was {extension}"))]
392    UnrecognizedCompression {
393        /// The file extension of the unrecognized file
394        extension: String,
395    },
396
397    /// Binaries were missing
398    #[error("failed to find bin {bin_name} for {pkg_name}")]
399    #[diagnostic(help("did the above build fail?"))]
400    MissingBinaries {
401        /// Name of package
402        pkg_name: String,
403        /// Name of binary
404        bin_name: String,
405    },
406
407    /// Error during `dist selfupdate`
408    #[error("`dist selfupdate` failed; the new version isn't in the place we expected")]
409    #[diagnostic(help("This is probably not your fault, please file an issue!"))]
410    UpdateFailed {},
411
412    /// Trying to run dist selfupdate in a random dir
413    #[error("`dist selfupdate` needs to be run in a project")]
414    #[diagnostic(help("If you just want to update dist and not your project, pass --skip-init"))]
415    UpdateNotInWorkspace {
416        /// The report about the missing workspace
417        #[diagnostic_source]
418        cause: axoproject::errors::ProjectError,
419    },
420
421    /// Trying to include CargoHome with other install paths
422    #[error("Incompatible install paths configured in Cargo.toml")]
423    #[diagnostic(help("The CargoHome `install-path` configuration can't be combined with other install path strategies."))]
424    IncompatibleInstallPathConfiguration,
425
426    /// Passed --artifacts but no --target
427    #[error("You specified --artifacts, disabling host mode, but specified no targets to build!")]
428    #[diagnostic(help("try adding --target={host_target}"))]
429    CliMissingTargets {
430        /// Current host target
431        host_target: TripleName,
432    },
433
434    /// Workspace isn't init
435    #[error("please run 'dist init' before running any other commands!")]
436    NeedsInit,
437
438    /// Running different version from config
439    #[error("You're running dist {running_version}, but 'cargo-dist-version = {config_version}' is set in your Cargo.toml")]
440    #[diagnostic(help("Rerun 'dist init' to update to this version."))]
441    MismatchedDistVersion {
442        /// config version
443        config_version: String,
444        /// running version
445        running_version: String,
446    },
447
448    /// Failed to make sense of 'cargo -vV'
449    #[error("Failed to get get toolchain version from 'cargo -vV'")]
450    FailedCargoVersion,
451
452    /// Failed to parse Github repo pair
453    #[error("Failed to parse github repo: {pair}")]
454    #[diagnostic(help("should be 'owner/repo' format"))]
455    GithubRepoPairParse {
456        /// The input
457        pair: String,
458    },
459
460    /// Unknown permission specified in GitHub Actions config
461    #[error("One or more unrecognized permissions levels were specified: {levels:?}")]
462    #[diagnostic(help("recognized values are: admin, write, read"))]
463    GithubUnknownPermission {
464        /// The input
465        levels: Vec<String>,
466    },
467
468    /// An unknown target was found
469    #[error("Unrecognized target: {target}")]
470    #[diagnostic(help("The full list of supported targets can be found here: https://axodotdev.github.io/cargo-dist/book/reference/config.html#targets"))]
471    UnrecognizedTarget {
472        /// The target in question
473        target: TripleName,
474    },
475
476    /// Installers requested despite having nothing to install
477    #[error("Installers were requested, but app contains no installable binaries")]
478    #[diagnostic(help(
479        "The only installable files are libraries, but `install-libraries` isn't enabled."
480    ))]
481    EmptyInstaller {},
482
483    /// Configuration value for github-build-setup defined but not found
484    #[error("failed to load github-build-setup file")]
485    GithubBuildSetupNotFound {
486        /// Inner i/o error with file path
487        #[diagnostic_source]
488        details: axoasset::AxoassetError,
489    },
490
491    /// Configuration value for github-build-setup defined but not found
492    #[error("github-build-setup file wasn't valid yaml")]
493    GithubBuildSetupParse {
494        /// Inner parse error with path and spans
495        #[diagnostic_source]
496        details: axoasset::AxoassetError,
497    },
498
499    /// github-build-setup file contents are invalid
500    #[error("github-build-setup file at {file_path} was invalid: {message}")]
501    #[diagnostic(help(
502        "For more details about writing build steps see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid"
503    ))]
504    GithubBuildSetupNotValid {
505        /// The value from the configuration file
506        file_path: Utf8PathBuf,
507        /// Error message details
508        message: String,
509    },
510
511    /// Something has metadata.dist but shouldn't
512    #[error("The metadata.dist entry in this Cargo.toml isn't being used:\n{manifest_path}")]
513    #[diagnostic(help(
514        "You probably want to move them to the [dist] section in\n{dist_manifest_path}"
515    ))]
516    UnusedMetadata {
517        /// The manifest that had the metadata.dist
518        manifest_path: Utf8PathBuf,
519        /// The dist.toml/dist-workspace.toml that means it's ignored
520        dist_manifest_path: Utf8PathBuf,
521    },
522
523    /// Build command looks like they put args in same string as command
524    #[error("Your build-command's arguments need to be split up\n{manifest}\ncommand was: [\"{command}\"]")]
525    #[diagnostic(help("the command should be split [\"like\", \"--this\", \"--array=here\"]"))]
526    SusBuildCommand {
527        /// path to manifest
528        manifest: Utf8PathBuf,
529        /// what the command was
530        command: String,
531    },
532
533    /// missing "dist" script in a package.json
534    #[error("package.json was missing a \"dist\" script\n{manifest}")]
535    #[diagnostic(help(
536        "https://axodotdev.github.io/cargo-dist/book/quickstart/javascript.html#adding-a-dist-script"
537    ))]
538    NoDistScript {
539        /// path to package.json
540        manifest: Utf8PathBuf,
541    },
542
543    /// Unsupported cross-compilation host/target combination
544    #[error("Cross-compilation from {host} to {target} is not supported")]
545    #[diagnostic(help("{details}"))]
546    UnsupportedCrossCompile {
547        /// The host system
548        host: Triple,
549        /// The target system
550        target: Triple,
551        /// Additional details about why this combination is unsupported
552        details: String,
553    },
554
555    /// Cannot use cross-compilation with cargo-auditable
556    #[error(
557        "Cross-compilation builds from {host} to {target} cannot be used with cargo-auditable"
558    )]
559    #[diagnostic(help("set cargo-auditable to false or don't do cross-compilation"))]
560    CannotDoCargoAuditableAndCrossCompile {
561        /// The host system
562        host: Triple,
563        /// The target system
564        target: Triple,
565    },
566
567    /// Generic build with Cargo-only build options
568    #[error("You're building a generic package but have a Cargo-only option enabled")]
569    #[diagnostic(help("Please disable the following from your configuration: {}", options.join(", ")))]
570    CargoOnlyBuildOptions {
571        /// The names of the invalid options
572        options: Vec<String>,
573    },
574
575    /// missing "build-command" for a package that needs one
576    #[error("dist package was missing a build-command\n{manifest}")]
577    #[diagnostic(help(
578        "https://axodotdev.github.io/cargo-dist/book/quickstart/everyone-else.html#setup"
579    ))]
580    NoBuildCommand {
581        /// path to manifest
582        manifest: Utf8PathBuf,
583    },
584
585    /// cargo package with build-command
586    #[error(
587        "cargo package was overridden with a build-command, which isn't supported yet\n{manifest}"
588    )]
589    UnexpectedBuildCommand {
590        /// path to manifest
591        manifest: Utf8PathBuf,
592    },
593
594    /// Failure to decode base64-encoded certificate
595    #[error("We failed to decode the certificate stored in the CODESIGN_CERTIFICATE environment variable.")]
596    #[diagnostic(help("Is the value of this environment variable valid base64?"))]
597    CertificateDecodeError {},
598
599    /// Missing configuration for a .pkg
600    #[error("A Mac .pkg installer was requested, but the config is missing")]
601    #[diagnostic(help("Please ensure a dist.mac-pkg-config section is present in your config. For more details see: https://example.com"))]
602    MacPkgConfigMissing {},
603
604    /// User left identifier empty in init
605    #[error("No bundle identifier was specified")]
606    #[diagnostic(help("Please either enter a bundle identifier, or disable the Mac .pkg"))]
607    MacPkgBundleIdentifierMissing {},
608
609    /// Project depends on a too-old axoupdater
610    #[error("Your project ({package_name}) uses axoupdater as a library, but the version specified ({your_version}) is older than the minimum supported version ({minimum}). (The dependency comes via {source_name} in the dependency tree.)")]
611    #[diagnostic(help(
612        "https://axodotdev.github.io/cargo-dist/book/installers/updater.html#minimum-supported-version-checking"
613    ))]
614    AxoupdaterTooOld {
615        /// Name of the package
616        package_name: String,
617        /// The package that depended on axoupdater
618        source_name: String,
619        /// Minimum supported version
620        minimum: semver::Version,
621        /// Version the project uses
622        your_version: semver::Version,
623    },
624}
625
626impl From<minijinja::Error> for DistError {
627    fn from(details: minijinja::Error) -> Self {
628        let source: String = details.template_source().unwrap_or_default().to_owned();
629        let span = details.range().map(|r| r.into()).or_else(|| {
630            details.line().map(|line| {
631                // some minijinja errors only have a line, not a range, so let's just highlight the whole line
632                let start = SourceOffset::from_location(&source, line, 0);
633                let end = SourceOffset::from_location(&source, line + 1, 0);
634                let len = (end.offset() - start.offset()).wrapping_sub(1);
635                SourceSpan::from((start, len))
636            })
637        });
638
639        DistError::Jinja {
640            source,
641            span,
642            backtrace: JinjaErrorWithBacktrace::new(details),
643        }
644    }
645}
646/// A struct that implements `std::error::Error` so it can be added as "related" to
647/// a miette diagnostic, and it'll show the backtrace.
648#[derive(Debug)]
649pub struct JinjaErrorWithBacktrace {
650    /// The original minijinja error
651    pub error: minijinja::Error,
652    /// The captured backtrace
653    backtrace: Backtrace,
654}
655
656impl JinjaErrorWithBacktrace {
657    fn new(error: minijinja::Error) -> Self {
658        Self {
659            error,
660            backtrace: Backtrace::new_unresolved(),
661        }
662    }
663}
664
665impl std::error::Error for JinjaErrorWithBacktrace {}
666
667impl std::fmt::Display for JinjaErrorWithBacktrace {
668    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669        let mut bt = self.backtrace.clone();
670        bt.resolve();
671        let backtrace = BacktracePrinter::new()
672            .add_frame_filter(Box::new(|frames| {
673                // try to find index of a frame that says `dist::real_main` and remove everything after it
674                if let Some(real_main_idx) = frames.iter().position(|f| {
675                    f.name
676                        .as_ref()
677                        .map(|n| n.contains("real_main"))
678                        .unwrap_or(false)
679                }) {
680                    frames.splice(real_main_idx + 1.., vec![]);
681                }
682            }))
683            .format_trace_to_string(&bt)
684            .unwrap();
685        write!(
686            f,
687            "Backtrace:\n{}\n{}",
688            style(&backtrace).dim(),
689            style(&self.error).red()
690        )
691    }
692}