sniff-cli 0.1.2

An exhaustive LLM-backed slop finder for codebases
Documentation
use super::paths::{resolve_module_path, same_path};
use super::{ResolveContext, SymbolGraph};
use crate::types::ResolvedSymbol;
use std::collections::HashSet;

fn resolved_rust_target(
    default_file: &str,
    symbol_name: &str,
    resolved: ResolvedSymbol,
) -> (String, String, String) {
    match resolved {
        ResolvedSymbol::Local(_) => (
            default_file.to_string(),
            symbol_name.to_string(),
            symbol_name.to_string(),
        ),
        ResolvedSymbol::External {
            file_path,
            symbol_name,
        } => (file_path, symbol_name.clone(), symbol_name),
    }
}

impl SymbolGraph {
    fn resolve_rust_direct_target(
        &self,
        ctx: &ResolveContext<'_>,
        segments: &[&str],
        visited: &mut HashSet<String>,
    ) -> Option<(String, String, String)> {
        let symbol_name = segments[segments.len() - 1];
        let module_prefix = segments[..segments.len() - 1].join("::");
        if !module_prefix.is_empty() {
            let module_file = resolve_module_path(ctx, &module_prefix)?;
            let resolved = self.resolve_symbol_in_file(ctx, &module_file, symbol_name, visited)?;
            return Some(resolved_rust_target(&module_file, symbol_name, resolved));
        }

        let resolved =
            self.resolve_symbol_in_file(ctx, ctx.importing_file, symbol_name, visited)?;
        Some(resolved_rust_target(
            ctx.importing_file,
            symbol_name,
            resolved,
        ))
    }

    fn resolve_rust_type_base(
        &self,
        ctx: &ResolveContext<'_>,
        segments: &[&str],
    ) -> Option<(String, String)> {
        let type_name = segments[segments.len() - 2];
        let base_prefix = if segments.len() > 2 {
            segments[..segments.len() - 2].join("::")
        } else {
            String::new()
        };
        let base_file = if base_prefix.is_empty() {
            ctx.importing_file.to_string()
        } else {
            resolve_module_path(ctx, &base_prefix)?
        };
        let current_symbols = self.files.get(ctx.importing_file)?;
        let base_resolved = if let Some(import) = current_symbols
            .imports
            .iter()
            .find(|import| import.local_name == type_name)
        {
            let imported_file = resolve_module_path(ctx, &import.source_module)?;
            let mut base_visited = HashSet::new();
            self.resolve_symbol_in_file(
                ctx,
                &imported_file,
                &import.imported_name,
                &mut base_visited,
            )?
        } else {
            let mut base_visited = HashSet::new();
            self.resolve_symbol_in_file(ctx, &base_file, type_name, &mut base_visited)?
        };

        Some(match base_resolved {
            ResolvedSymbol::Local(_) => (base_file, type_name.to_string()),
            ResolvedSymbol::External {
                file_path,
                symbol_name,
            } => (file_path, symbol_name),
        })
    }

    pub(super) fn resolve_rust_type_member(
        &self,
        ctx: &ResolveContext<'_>,
        type_file: &str,
        resolved_type_name: &str,
        member_name: &str,
    ) -> Option<ResolvedSymbol> {
        let type_symbols = self.files.get(type_file)?;
        let member_def =
            self.find_definition(type_symbols, member_name, Some(resolved_type_name))?;

        if same_path(ctx.importing_file, type_file) {
            Some(ResolvedSymbol::Local(member_def.id))
        } else {
            Some(ResolvedSymbol::External {
                file_path: type_file.to_string(),
                symbol_name: member_def.name.clone(),
            })
        }
    }

    pub(super) fn resolve_rust_type_target(
        &self,
        ctx: &ResolveContext<'_>,
        segments: &[&str],
        visited: &mut HashSet<String>,
    ) -> Option<(String, String, String)> {
        let member_name = segments[segments.len() - 1];
        if let Some(target) = self.resolve_rust_direct_target(ctx, segments, visited) {
            return Some(target);
        }

        let (type_file, resolved_type_name) = self.resolve_rust_type_base(ctx, segments)?;

        Some((type_file, resolved_type_name, member_name.to_string()))
    }
}