scoped-error 0.1.4

Structured error handling with semantic context trees.
Documentation
// Copyright (C) 2026 Kan-Ru Chen <kanru@kanru.info>
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

use scoped_error::{Many, expect_error_fn};
use std::{error::Error, thread};

fn fetch_all(urls: Vec<String>) -> Result<Vec<String>, Many> {
    let handles: Vec<_> = urls
        .into_iter()
        .map(|url| thread::spawn(move || fetch(&url)))
        .collect();

    let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();

    Many::from_results("failed to fetch URLs", results)
}

fn fetch(url: &str) -> Result<String, scoped_error::Error> {
    let err = || scoped_error::Error::new(format!("failed to fetch {}", url));

    expect_error_fn(err, || {
        connect(url)?;
        Ok("connected".into())
    })
}

fn connect(url: &str) -> Result<String, scoped_error::Error> {
    let err = || scoped_error::Error::new(format!("failed to connect to {}", url));

    expect_error_fn(err, || Err("no network connection")?)
}

fn main() -> Result<(), Box<dyn Error>> {
    fetch_all(vec![
        "https://example.com".to_string(),
        "https://example.org".to_string(),
    ])?;
    Ok(())
}