mini-build 0.1.0

Builds the directory a static server serves: CSS/JS bundling and minification via external tools, plus asset mirroring.
Documentation
use super::*;

use std::io::{Error as IoError, ErrorKind};

/// The four variants exist to separate "you configured this wrong" from "the build
/// failed", so their messages must actually say which happened.
#[test]
fn each_variant_names_what_went_wrong() {
    assert_eq!(
        BuildError::Io(IoError::new(ErrorKind::NotFound, "no such file")).to_string(),
        "io error: no such file"
    );
    assert_eq!(
        BuildError::Config("folders overlap".into()).to_string(),
        "invalid configuration: folders overlap"
    );
    assert_eq!(
        BuildError::ToolMissing("esbuild not found on PATH".into()).to_string(),
        "required tool missing: esbuild not found on PATH"
    );
}

/// Only the wrapping variants have an underlying cause to hand a caller walking the
/// chain; the string variants are the whole story.
#[test]
fn only_the_wrapping_variants_expose_a_source() {
    use std::error::Error as _;

    let io = BuildError::Io(IoError::other("inner"));
    assert_eq!(io.source().map(ToString::to_string), Some("inner".into()));

    assert!(BuildError::Config("m".into()).source().is_none());
    assert!(BuildError::ToolMissing("m".into()).source().is_none());
}

#[test]
fn an_io_error_converts_preserving_its_kind() {
    let error: BuildError = IoError::new(ErrorKind::PermissionDenied, "denied").into();

    let BuildError::Io(inner) = &error else {
        panic!("io::Error must convert into BuildError::Io, got: {error}");
    };
    assert_eq!(inner.kind(), ErrorKind::PermissionDenied);
}