kibble 0.1.0

chew through any source into clean datasets — a fast ingestion, RAG & fine-tuning toolkit
Documentation
//! Exercises kibble as an external library consumer would (issue #59) — no subprocess.

#[tokio::test]
async fn search_is_callable_as_a_library() {
    // A dir with no built index → search returns a clean Err (missing index),
    // reached as a typed library call rather than by shelling out to the binary.
    let dir = std::env::temp_dir().join(format!("kibble_lib_search_{}", std::process::id()));
    std::fs::create_dir_all(&dir).unwrap();
    let res: std::io::Result<Vec<kibble::retrieve::Hit>> =
        kibble::retrieve::search(&dir, "anything", 5).await;
    std::fs::remove_dir_all(&dir).ok();
    assert!(res.is_err(), "expected a missing-index Err, got {res:?}");
}

#[tokio::test]
async fn query_returns_clean_err_when_base_url_unset() {
    // No kibble.toml → [ask].base_url empty → query must return the same clean Err
    // the CLI surfaces (issue #60 contract), as a typed library result — never a panic.
    let dir = std::env::temp_dir().join(format!("kibble_lib_query_{}", std::process::id()));
    std::fs::create_dir_all(&dir).unwrap();
    let out: std::io::Result<kibble::ask::Outcome> =
        kibble::ask::query(&dir, "what is a stack canary?", kibble::ask::AskOptions::default()).await;
    std::fs::remove_dir_all(&dir).ok();
    let err = out.expect_err("expected a clean Err for unset base_url");
    assert!(err.to_string().contains("base_url"), "unexpected error: {err}");
}

#[test]
fn query_future_is_send() {
    // A library consumer with a Send-bound async context (e.g. collar's #[async_trait]
    // methods, awaited from a tokio::spawn'd task) must be able to .await query directly.
    // query always uses StreamMode::Off, so its future must be genuinely Send (#63).
    // Compile-time assertion — the future is constructed but never awaited (no I/O).
    fn assert_send<T: Send>(_: &T) {}
    let dir = std::env::temp_dir();
    let fut = kibble::ask::query(&dir, "q", kibble::ask::AskOptions::default());
    assert_send(&fut);
}

#[test]
fn query_stream_is_send() {
    fn assert_send<T: Send>(_: &T) {}
    let dir = std::env::temp_dir();
    // Err path (no base_url) — but the returned Ok stream type must be Send; assert on a typed binding.
    let s = kibble::ask::query_stream(&dir, "q", kibble::ask::AskOptions::default());
    assert_send(&s); // io::Result<impl Stream + Send> is Send
}

#[tokio::test]
async fn query_stream_errors_cleanly_without_base_url() {
    let dir = std::env::temp_dir().join(format!("kibble_stream_{}", std::process::id()));
    std::fs::create_dir_all(&dir).unwrap();
    let res = kibble::ask::query_stream(&dir, "q", kibble::ask::AskOptions::default());
    std::fs::remove_dir_all(&dir).ok();
    let err = res.err().expect("unset base_url must be a clean Err before streaming");
    assert!(err.to_string().contains("base_url"), "unexpected error: {err}");
}