symbolique 0.1.0

Symbol table pipeline for language servers — parse, link, merge, and resolve symbols across files, built on the laburnum LSP framework.
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! Shared CAS partition for symbol shapes.
//!
//! This module defines the `Symbols` partition which stores content-addressed
//! symbol shapes shared across all stages. Following ADR0003/ADR0005, symbol
//! shapes are deduplicated in this single CAS partition, while stage-specific
//! partitions (FileSymbols, LinkedSymbols, etc.) maintain index entries that
//! reference shapes via `RecordHandle<Symbols<V, I, P>>`.
//!
//! # Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────────┐
//! │                    Symbols<V, I, P>                             │
//! │                    (CAS partition)                              │
//! │                                                                 │
//! │  Stores: Symbol<V, I, P> enum                                   │
//! │  IndexEntry: ()                                                 │
//! │  Deduplicated across all files and stages                       │
//! └─────────────────────────────────────────────────────────────────┘
//!//!                               │ RecordHandle<Symbols<V, I, P>>
//!//! ┌──────────────┬──────────────┼──────────────┬────────────────────┐
//! │ FileSymbols  │ LinkedSymbols│              │ SymbolResolution   │
//! │ (index-only) │ (index-only) │ MergedSymbols│ (index-only)       │
//! │              │              │ (index-only) │                    │
//! │ SymbolEntry  │ SymbolEntry  │ SymbolEntry  │ ResolutionEntry    │
//! └──────────────┴──────────────┴──────────────┴────────────────────┘
//! ```

use {
  crate::core::{Ident, Symbol, SymbolPath, Value},
  laburnum::{
    Ident as LaburnumIdent,
    database::{Partition, PartitionKey},
  },
  std::{hash::Hash, marker::PhantomData},
};

/// CAS partition for symbol shapes (shared across all stages).
///
/// This partition stores the semantic content of symbols - kind, name,
/// visibility, value - without location information. Shapes are content-
/// addressed and deduplicated: two identical function signatures share one
/// CAS record regardless of where they appear.
///
/// # Type Parameters
///
/// - `V`: Value type for literals
/// - `I`: Identifier type for names
/// - `P`: Symbol path type (used in references and imports)
///
/// # Usage
///
/// Writers use `store::<Symbols<V, I, P>>()` to add shapes to this partition.
/// The returned `RecordHandle<Symbols<V, I, P>>` is then used in index entries
/// for stage-specific partitions.
///
/// ```ignore
/// // Store shape in Symbols partition (CAS, deduplicated by content)
/// let symbol = Symbol::Definition { name, value, visibility };
/// let handle = writer.store::<Symbols<V, I, P>>(symbol);
///
/// // Create index entry in FileSymbols partition (keyed by path)
/// let entry = SymbolEntry { span, symbol: handle };
/// writer.index_entry::<FileSymbols<V, I, P>>(path.to_string(), entry);
/// ```
#[derive(Hash)]
pub struct Symbols<V, I, P>(PhantomData<(V, I, P)>);

impl<V, I, P> Default for Symbols<V, I, P> {
  fn default() -> Self {
    Self(PhantomData)
  }
}

impl<V, I, P> Clone for Symbols<V, I, P> {
  fn clone(&self) -> Self {
    *self
  }
}

impl<V, I, P> Copy for Symbols<V, I, P> {}

impl<V, I, P> std::fmt::Debug for Symbols<V, I, P> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.debug_struct("Symbols").finish()
  }
}

impl<V, I, P> PartitionKey for Symbols<V, I, P>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
{
  const KEY: LaburnumIdent = LaburnumIdent::new("symbolique::symbols");
}

impl<V, I, P> Partition for Symbols<V, I, P>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
{
  type Record = Symbol<V, I, P>;
  type IndexEntry = (); // CAS-only, no index entries
  type SortKey = String;

  fn index_entry_from_handle(
    _handle: laburnum::database::RecordHandle<Self>,
  ) -> Self::IndexEntry {
    // No-op for CAS-only partitions
  }
}

// =============================================================================
// Record implementations
// =============================================================================

// Note: Record implementation for Symbol<V, I, P> is already in file.rs
// We should move it here eventually, but for now we'll leave it to avoid
// breaking existing code.

#[cfg(test)]
mod tests {
  use super::*;

  /// Simple path type for testing
  #[derive(Debug, Clone, Hash, PartialEq, Eq)]
  struct TestPath(String);

  impl crate::core::SymbolPath for TestPath {
    fn sort_key(&self) -> String {
      self.0.clone()
    }
  }

  #[test]
  fn symbols_partition_key() {
    use crate::core::{DefaultValue, StringIdent};

    // Verify the partition key is defined
    let key = Symbols::<DefaultValue, StringIdent, TestPath>::KEY;
    assert_eq!(key, laburnum::Ident::new("symbolique::symbols"));
  }

  #[test]
  fn symbols_is_cas_only() {
    use crate::core::{DefaultValue, StringIdent};

    // Verify IndexEntry is () (CAS-only partition) by checking
    // the partition compiles with IndexEntry = ()
    fn assert_index_entry_is_unit<P: Partition<IndexEntry = ()>>() {}

    assert_index_entry_is_unit::<Symbols<DefaultValue, StringIdent, TestPath>>(
    );
  }
}