gobby-code 0.9.9

Fast Rust CLI for Gobby's code index — AST-aware search, symbol navigation, and dependency graph
Documentation
use super::contracts::NAMESPACE;
use super::identifiers::qualified_relation;
use gobby_core::setup::{
    OwnedObject, SetupContext, SetupError, SetupReport, StandaloneSetup, StoreKind,
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GcodeStandaloneSetup {
    schema: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PostgresObjectDefinition {
    pub(crate) name: String,
    pub(crate) sql: String,
}

impl GcodeStandaloneSetup {
    pub fn new(schema: impl Into<String>) -> Self {
        Self {
            schema: schema.into(),
        }
    }

    pub fn schema(&self) -> &str {
        &self.schema
    }

    fn object_definition(&self, name: &str, sql: String) -> PostgresObjectDefinition {
        PostgresObjectDefinition {
            name: name.to_string(),
            sql,
        }
    }

    fn qualified(&self, relation: &str) -> Result<String, SetupError> {
        qualified_relation(&self.schema, relation, "relation")
    }

    pub(crate) fn postgres_object_definitions(
        &self,
    ) -> Result<Vec<PostgresObjectDefinition>, SetupError> {
        let code_indexed_projects = self.qualified("code_indexed_projects")?;
        let code_indexed_files = self.qualified("code_indexed_files")?;
        let code_symbols = self.qualified("code_symbols")?;
        let code_content_chunks = self.qualified("code_content_chunks")?;
        let code_imports = self.qualified("code_imports")?;
        let code_calls = self.qualified("code_calls")?;

        Ok(vec![
            self.object_definition(
                "pg_search extension",
                "CREATE EXTENSION IF NOT EXISTS pg_search;".to_string(),
            ),
            self.object_definition(
                "code_indexed_projects table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_indexed_projects} (
                        id TEXT PRIMARY KEY,
                        root_path TEXT NOT NULL,
                        total_files INTEGER NOT NULL DEFAULT 0,
                        total_symbols INTEGER NOT NULL DEFAULT 0,
                        last_indexed_at TIMESTAMPTZ,
                        index_duration_ms INTEGER,
                        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
                        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
                    );"
                ),
            ),
            self.object_definition(
                "code_indexed_files table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_indexed_files} (
                        id TEXT PRIMARY KEY,
                        project_id TEXT NOT NULL,
                        file_path TEXT NOT NULL,
                        language TEXT NOT NULL,
                        content_hash TEXT NOT NULL,
                        symbol_count INTEGER NOT NULL DEFAULT 0,
                        byte_size INTEGER NOT NULL DEFAULT 0,
                        graph_synced BOOLEAN NOT NULL DEFAULT FALSE,
                        vectors_synced BOOLEAN NOT NULL DEFAULT FALSE,
                        graph_sync_attempted_at TIMESTAMPTZ,
                        indexed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
                        UNIQUE (project_id, file_path)
                    );"
                ),
            ),
            self.object_definition(
                "idx_cif_project index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cif_project
                     ON {code_indexed_files}(project_id);"
                ),
            ),
            self.object_definition(
                "idx_cif_graph_synced index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cif_graph_synced
                     ON {code_indexed_files}(project_id, graph_synced);"
                ),
            ),
            self.object_definition(
                "idx_cif_vectors_synced index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cif_vectors_synced
                     ON {code_indexed_files}(project_id, vectors_synced);"
                ),
            ),
            self.object_definition(
                "code_symbols table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_symbols} (
                        id TEXT PRIMARY KEY,
                        project_id TEXT NOT NULL,
                        file_path TEXT NOT NULL,
                        name TEXT NOT NULL,
                        qualified_name TEXT NOT NULL,
                        kind TEXT NOT NULL,
                        language TEXT NOT NULL,
                        byte_start INTEGER NOT NULL,
                        byte_end INTEGER NOT NULL,
                        line_start INTEGER NOT NULL,
                        line_end INTEGER NOT NULL,
                        signature TEXT,
                        docstring TEXT,
                        parent_symbol_id TEXT,
                        content_hash TEXT NOT NULL,
                        summary TEXT,
                        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
                        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
                    );"
                ),
            ),
            self.object_definition(
                "idx_cs_project index",
                format!("CREATE INDEX IF NOT EXISTS idx_cs_project ON {code_symbols}(project_id);"),
            ),
            self.object_definition(
                "idx_cs_file index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cs_file
                     ON {code_symbols}(project_id, file_path);"
                ),
            ),
            self.object_definition(
                "idx_cs_name index",
                format!("CREATE INDEX IF NOT EXISTS idx_cs_name ON {code_symbols}(name);"),
            ),
            self.object_definition(
                "idx_cs_qualified index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cs_qualified
                     ON {code_symbols}(qualified_name);"
                ),
            ),
            self.object_definition(
                "idx_cs_kind index",
                format!("CREATE INDEX IF NOT EXISTS idx_cs_kind ON {code_symbols}(kind);"),
            ),
            self.object_definition(
                "idx_cs_parent index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cs_parent
                     ON {code_symbols}(parent_symbol_id);"
                ),
            ),
            self.object_definition(
                "code_content_chunks table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_content_chunks} (
                        id TEXT PRIMARY KEY,
                        project_id TEXT NOT NULL,
                        file_path TEXT NOT NULL,
                        chunk_index INTEGER NOT NULL,
                        line_start INTEGER NOT NULL,
                        line_end INTEGER NOT NULL,
                        content TEXT NOT NULL,
                        language TEXT,
                        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
                        UNIQUE (project_id, file_path, chunk_index)
                    );"
                ),
            ),
            self.object_definition(
                "idx_ccc_project index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_ccc_project
                     ON {code_content_chunks}(project_id);"
                ),
            ),
            self.object_definition(
                "idx_ccc_file index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_ccc_file
                     ON {code_content_chunks}(project_id, file_path);"
                ),
            ),
            self.object_definition(
                "code_imports table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_imports} (
                        id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
                        project_id TEXT NOT NULL,
                        source_file TEXT NOT NULL,
                        target_module TEXT NOT NULL,
                        UNIQUE (project_id, source_file, target_module)
                    );"
                ),
            ),
            self.object_definition(
                "idx_ci_file index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_ci_file
                     ON {code_imports}(project_id, source_file);"
                ),
            ),
            self.object_definition(
                "code_calls table",
                format!(
                    "CREATE TABLE IF NOT EXISTS {code_calls} (
                        id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
                        project_id TEXT NOT NULL,
                        caller_symbol_id TEXT NOT NULL,
                        callee_symbol_id TEXT NOT NULL DEFAULT '',
                        callee_name TEXT NOT NULL,
                        callee_target_kind TEXT NOT NULL DEFAULT 'unresolved',
                        callee_external_module TEXT NOT NULL DEFAULT '',
                        file_path TEXT NOT NULL,
                        line INTEGER NOT NULL DEFAULT 0,
                        UNIQUE (
                            project_id, caller_symbol_id, callee_symbol_id, callee_name,
                            callee_target_kind, callee_external_module, file_path, line
                        )
                    );"
                ),
            ),
            self.object_definition(
                "idx_cc_file index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cc_file
                     ON {code_calls}(project_id, file_path);"
                ),
            ),
            self.object_definition(
                "idx_cc_caller index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cc_caller
                     ON {code_calls}(project_id, caller_symbol_id);"
                ),
            ),
            self.object_definition(
                "idx_cc_target index",
                format!(
                    "CREATE INDEX IF NOT EXISTS idx_cc_target
                     ON {code_calls}(project_id, callee_target_kind, callee_symbol_id, callee_name);"
                ),
            ),
            self.object_definition(
                "code_symbols_search_bm25 index",
                format!(
                    "CREATE INDEX IF NOT EXISTS code_symbols_search_bm25
                     ON {code_symbols}
                     USING bm25 (id, name, qualified_name, signature, docstring, summary)
                     WITH (key_field = 'id');"
                ),
            ),
            self.object_definition(
                "code_content_search_bm25 index",
                format!(
                    "CREATE INDEX IF NOT EXISTS code_content_search_bm25
                     ON {code_content_chunks}
                     USING bm25 (id, content)
                     WITH (key_field = 'id');"
                ),
            ),
        ])
    }
}

impl StandaloneSetup for GcodeStandaloneSetup {
    fn namespace(&self) -> &str {
        NAMESPACE
    }

    fn owned_objects(&self) -> Result<Vec<OwnedObject>, SetupError> {
        Ok(self
            .postgres_object_definitions()?
            .into_iter()
            .map(owned_object)
            .collect())
    }

    fn create(&self, ctx: &mut SetupContext<'_>) -> Result<SetupReport, SetupError> {
        let mut report = SetupReport::default();
        let mut objects = self.owned_objects()?.into_iter();
        while let Some(mut object) = objects.next() {
            match (object.creator)(ctx) {
                Ok(()) => report.created.push(object.name),
                Err(err) => {
                    report.failed.push((object.name, err.to_string()));
                    report.skipped.extend(objects.map(|object| object.name));
                    break;
                }
            }
        }
        Ok(report)
    }
}

fn owned_object(definition: PostgresObjectDefinition) -> OwnedObject {
    let object_name = definition.name;
    let sql = definition.sql;
    OwnedObject {
        name: object_name.clone(),
        store: StoreKind::Postgres,
        creator: Box::new(move |ctx| execute_postgres_ddl(ctx, &object_name, &sql)),
    }
}

fn execute_postgres_ddl(
    ctx: &mut SetupContext<'_>,
    object: &str,
    sql: &str,
) -> Result<(), SetupError> {
    let Some(pg) = ctx.pg.as_deref_mut() else {
        return Err(SetupError::ConnectionFailed {
            store: "postgres".to_string(),
            message: "PostgreSQL connection was not supplied to setup context".to_string(),
        });
    };

    pg.batch_execute(sql)
        .map_err(|err| SetupError::CreationFailed {
            object: object.to_string(),
            message: err.to_string(),
        })
}