Documentation
// Example for using locations and backtraces

// When the "backtrace-external" feature is enabled, we will be using
// https://github.com/rust-lang/backtrace-rs

// It is re-exported as problemo::backtrace

use {
    anstream::println,
    owo_colors::*,
    problemo::{backtrace::*, common::*, *},
    std::panic::*,
};

// We're explicitly attaching a location and backtrace here. But you can set the environment
// variables PROBLEMO_LOCATION and/or PROBLEMO_BACKTRACE to anything other than "0" to
// auto-attach them to all problems.

// Note the #[track_caller] annotation on this function. By adding it, we are making sure that this
// function will *not* be used for the Location. Try removing it to see how the result changes!
//
// See: https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute

#[track_caller]
fn read_file(path: &str) -> Result<String, Problem> {
    std::fs::read_to_string(path)
        .via(LowLevelError)
        .with_location()
        .with_backtrace()
}

fn main() {
    if let Err(problem) = read_file("non-existing.txt") {
        if let Some(location) = problem.attachment_of_type::<Location>() {
            println!("location:");
            println!(
                "{} @ {}:{}",
                location.file().yellow(),
                location.line().red(),
                location.column().red()
            );
        }

        // The external backtrace library is similar to std::backtrace::Backtrace,
        // while also allowing us to inspect the frames

        if let Some(backtrace) = problem.attachment_of_type::<Backtrace>() {
            println!("backtrace:");
            for frame in backtrace.frames() {
                for symbol in frame.symbols() {
                    if let Some(name) = symbol.name() {
                        println!("{}", name.blue());
                    }
                    if let Some(filename) = symbol.filename() {
                        println!("  {}{}", filename.display().yellow(), location(symbol));
                    }
                }
            }
        }
    }
}

fn location(symbol: &BacktraceSymbol) -> String {
    if let Some(lineno) = symbol.lineno()
        && let Some(colno) = symbol.colno()
    {
        format!(" @ {}:{}", lineno.red(), colno.red())
    } else {
        Default::default()
    }
}