scryer-engine 0.1.0

Tree-sitter and stack-graphs AST indexing engine for Scryer code intelligence
use std::path::Path;

use crate::payload::ParsedFilePayload;

pub mod python;
pub mod rust;
pub mod typescript;

pub use python::PythonAstParser;
pub use rust::{
    ExtractedUseItem, RustAstParser, crate_root_for, dependency_module_path_for,
    extract_type_members, extract_use_items, module_path_for,
};
pub use typescript::TypeScriptAstParser;

/// Parsing mode distinguishing full workspace parsing from public surface external dependency extraction.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ParseMode {
    #[default]
    Workspace,
    DependencyPublicSurface {
        crate_name: String,
    },
}

impl ParseMode {
    pub fn is_dependency_public_surface(&self) -> bool {
        matches!(self, Self::DependencyPublicSurface { .. })
    }

    pub fn crate_name(&self) -> Option<&str> {
        match self {
            Self::Workspace => None,
            Self::DependencyPublicSurface { crate_name } => Some(crate_name.as_str()),
        }
    }
}

/// Automatically detect language from file extension and parse into `ParsedFilePayload`.
pub fn parse_file(
    relative_path: &Path,
    content: &[u8],
    content_hash: &str,
    mode: ParseMode,
) -> anyhow::Result<ParsedFilePayload> {
    let ext = relative_path
        .extension()
        .and_then(|e| e.to_str())
        .unwrap_or("")
        .to_ascii_lowercase();

    match ext.as_str() {
        "rs" => RustAstParser::parse(relative_path, content, content_hash, mode),
        "py" => PythonAstParser::parse(relative_path, content, content_hash),
        "ts" | "tsx" | "js" | "jsx" => {
            TypeScriptAstParser::parse(relative_path, content, content_hash)
        }
        other => anyhow::bail!(
            "Unsupported file extension '{other}' for {}",
            relative_path.display()
        ),
    }
}