delta_kernel 0.30.0

Core crate providing a Delta/Deltalake implementation focused on interoperability with a wide range of query engines.
Documentation
use std::backtrace::Backtrace;
use std::error::Error as StdError;
use std::io;
use std::num::{IntErrorKind, ParseIntError};

use delta_kernel::{
    Error, KernelError, KernelResult, KernelResultIterator, KernelResultIteratorStatic, Result,
    ResultIterator, ResultIteratorStatic,
};
use rstest::rstest;

#[rstest]
#[case::without_source(KernelError::file_not_found("missing.parquet"))]
#[case::with_source(KernelError::generic_err(io::Error::other("read failed")))]
#[case::with_backtrace(KernelError::Backtraced {
    source: Box::new(KernelError::file_not_found("missing.parquet")),
    backtrace: Box::new(Backtrace::disabled()),
})]
fn test_error_preserves_kernel_display_and_source(#[case] kernel: KernelError) {
    let expected_display = kernel.to_string();
    let error = Error::Kernel(kernel);
    assert_eq!(error.to_string(), expected_display);

    let Error::Kernel(kernel) = &error else {
        panic!("expected a kernel error");
    };
    match (error.source(), kernel.source()) {
        (Some(actual), Some(expected)) => assert!(std::ptr::eq(actual, expected)),
        (None, None) => {}
        _ => panic!("source changed when wrapping the kernel error"),
    }
    if let KernelError::GenericError { .. } = kernel {
        let source = error.source().unwrap().downcast_ref::<io::Error>().unwrap();
        assert_eq!(source.to_string(), "read failed");
    }
}

#[test]
fn test_kernel_result_explicitly_maps_into_error() {
    let propagate = |result: KernelResult<()>| -> Result<()> {
        result.map_err(Error::Kernel)?;
        Ok(())
    };

    assert!(propagate(Ok(())).is_ok());
    let error = propagate(Err(KernelError::file_not_found("missing.parquet"))).unwrap_err();
    assert!(
        matches!(error, Error::Kernel(KernelError::FileNotFound(path)) if path == "missing.parquet")
    );
}

#[test]
fn test_kernel_result_preserves_custom_error() {
    let result: KernelResult<u64, ParseIntError> = "invalid".parse();
    assert_eq!(result.unwrap_err().kind(), &IntErrorKind::InvalidDigit);
}

#[test]
fn test_kernel_result_iterator_explicitly_maps_into_error() {
    let kernel: KernelResultIteratorStatic<()> = Box::new(std::iter::once(Err(
        KernelError::file_not_found("missing.parquet"),
    )));
    let kernel: KernelResultIterator<'_, ()> = kernel;
    let result: ResultIteratorStatic<()> = Box::new(kernel.map(|item| item.map_err(Error::Kernel)));
    let mut result: ResultIterator<'_, ()> = result;
    assert!(matches!(
        result.next().unwrap(),
        Err(Error::Kernel(KernelError::FileNotFound(path))) if path == "missing.parquet"
    ));
    assert!(result.next().is_none());
}

#[rstest]
#[case::unwrapped(0)]
#[case::one_wrapper(1)]
#[case::nested_wrappers(2)]
fn test_kernel_error_without_backtrace(#[case] wrapper_count: usize) {
    let mut error = KernelError::file_not_found("missing.parquet");
    for _ in 0..wrapper_count {
        error = KernelError::Backtraced {
            source: Box::new(error),
            backtrace: Box::new(Backtrace::disabled()),
        };
    }

    assert!(
        matches!(error.without_backtrace(), KernelError::FileNotFound(path) if path == "missing.parquet")
    );
}

#[test]
fn test_error_trait_bounds() {
    fn assert_error<T: StdError + Send + Sync + 'static>() {}
    assert_error::<Error>();
}