collet 0.1.1

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
// ---------------------------------------------------------------------------
// URI helpers
// ---------------------------------------------------------------------------

use super::super::protocol::{DocumentSymbol, SymbolKind};
use crate::repo_map::parser;

/// Convert a filesystem path to a `file://` URI.
pub fn path_to_uri(path: &str) -> String {
    if path.starts_with('/') {
        format!("file://{path}")
    } else {
        // Windows-style or relative — add leading slash.
        format!("file:///{path}")
    }
}

/// Convert a `file://` URI back to a filesystem path.
pub fn uri_to_path(uri: &str) -> String {
    if let Some(rest) = uri.strip_prefix("file://") {
        // On Unix the path starts with `/`, so rest already has it.
        // On Windows `file:///C:/...` → strip the extra leading slash.
        if rest.starts_with('/') && rest.len() > 2 && rest.as_bytes()[2] == b':' {
            // Windows path — drop leading `/`.
            rest[1..].to_string()
        } else {
            rest.to_string()
        }
    } else {
        uri.to_string()
    }
}

// ---------------------------------------------------------------------------
// Conversion to repo_map symbols
// ---------------------------------------------------------------------------

/// Convert LSP `DocumentSymbol`s into the `repo_map::parser::Symbol` type
/// used by the existing repo map infrastructure.
pub fn to_repo_symbols(symbols: &[DocumentSymbol], file_path: &str) -> Vec<parser::Symbol> {
    let mut out = Vec::new();
    flatten_symbols(symbols, &mut out, file_path);
    out
}

pub(super) fn flatten_symbols(
    symbols: &[DocumentSymbol],
    out: &mut Vec<parser::Symbol>,
    _file_path: &str,
) {
    for sym in symbols {
        if let Some(kind) = map_symbol_kind(sym.kind) {
            out.push(parser::Symbol {
                name: sym.name.clone(),
                kind,
                line: (sym.selection_range.start.line as usize) + 1,
                signature: sym.detail.clone(),
            });
        }

        if let Some(children) = &sym.children {
            flatten_symbols(children, out, _file_path);
        }
    }
}

/// Map an LSP `SymbolKind` to the repo-map `parser::SymbolKind`.
/// Returns `None` for kinds that have no direct equivalent.
pub(super) fn map_symbol_kind(kind: SymbolKind) -> Option<parser::SymbolKind> {
    match kind {
        SymbolKind::Function | SymbolKind::Method | SymbolKind::Constructor => {
            Some(parser::SymbolKind::Function)
        }
        SymbolKind::Class | SymbolKind::Struct => Some(parser::SymbolKind::Struct),
        SymbolKind::Enum => Some(parser::SymbolKind::Enum),
        SymbolKind::Interface => Some(parser::SymbolKind::Trait),
        SymbolKind::Module | SymbolKind::Namespace | SymbolKind::Package => {
            Some(parser::SymbolKind::Mod)
        }
        SymbolKind::Constant => Some(parser::SymbolKind::Const),
        SymbolKind::TypeParameter => Some(parser::SymbolKind::Type),
        _ => None,
    }
}