ralph-coder 0.2.1

An agentic code generation CLI powered by multiple LLM backends
Documentation
use ralph::symbol_index::SymbolIndex;
use std::fs;
use tempfile::TempDir;

fn write(dir: &TempDir, name: &str, content: &str) {
    let path = dir.path().join(name);
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).unwrap();
    }
    fs::write(path, content).unwrap();
}

#[test]
fn finds_rust_function() {
    let dir = TempDir::new().unwrap();
    write(&dir, "lib.rs", "pub fn hello_world() {}\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("hello_world", 10);
    assert!(!results.is_empty());
    assert_eq!(results[0].name, "hello_world");
}

#[test]
fn finds_rust_struct() {
    let dir = TempDir::new().unwrap();
    write(&dir, "models.rs", "pub struct FooBar { x: u32 }\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("FooBar", 10);
    assert!(!results.is_empty());
    assert_eq!(results[0].name, "FooBar");
}

#[test]
fn find_is_case_insensitive() {
    let dir = TempDir::new().unwrap();
    write(&dir, "lib.rs", "pub fn MyFunction() {}\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("myfunction", 10);
    assert!(!results.is_empty());
}

#[test]
fn find_returns_substring_matches() {
    let dir = TempDir::new().unwrap();
    write(
        &dir,
        "lib.rs",
        "pub fn process_request() {}\npub fn process_response() {}\n",
    );
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("process", 10);
    assert!(results.len() >= 2);
}

#[test]
fn find_returns_empty_for_unknown_name() {
    let dir = TempDir::new().unwrap();
    write(&dir, "lib.rs", "pub fn foo() {}\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("zzz_not_found", 10);
    assert!(results.is_empty());
}

#[test]
fn read_body_returns_source() {
    let dir = TempDir::new().unwrap();
    write(
        &dir,
        "lib.rs",
        "pub fn greet() {\n    println!(\"hi\");\n}\n",
    );
    let idx = SymbolIndex::build(dir.path());
    let body = idx.read_body("greet");
    assert!(body.is_some());
    let body = body.unwrap();
    assert!(body.contains("greet"));
    assert!(body.contains("println"));
}

#[test]
fn read_body_returns_none_for_unknown() {
    let dir = TempDir::new().unwrap();
    write(&dir, "lib.rs", "pub fn foo() {}\n");
    let idx = SymbolIndex::build(dir.path());
    assert!(idx.read_body("not_a_real_symbol").is_none());
}

#[test]
fn symbol_count_reflects_indexed_symbols() {
    let dir = TempDir::new().unwrap();
    write(
        &dir,
        "lib.rs",
        "pub fn a() {}\npub fn b() {}\npub struct C {}\n",
    );
    let idx = SymbolIndex::build(dir.path());
    assert!(idx.symbol_count() >= 3);
}

#[test]
fn finds_python_function() {
    let dir = TempDir::new().unwrap();
    write(&dir, "main.py", "def my_py_func():\n    pass\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("my_py_func", 10);
    assert!(!results.is_empty());
}

#[test]
fn finds_typescript_function() {
    let dir = TempDir::new().unwrap();
    write(&dir, "index.ts", "export function tsFunc() { return 1; }\n");
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("tsFunc", 10);
    assert!(!results.is_empty());
}

#[test]
fn find_limit_is_respected() {
    let dir = TempDir::new().unwrap();
    let fns: String = (0..20)
        .map(|i| format!("pub fn foo_func_{i}() {{}}\n"))
        .collect();
    write(&dir, "lib.rs", &fns);
    let idx = SymbolIndex::build(dir.path());
    let results = idx.find("foo_func", 5);
    assert_eq!(results.len(), 5);
}