laburnum 1.17.0

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use crate::{Ident, source::SourceKey};
use sealed::sealed;

/// Sealed marker trait for sort key types. External crates cannot implement
/// this trait, which prevents them from interacting with `SortKey` through
/// any trait-based mechanism.
#[sealed]
pub trait SortKeySealed {}

/// Opaque wrapper around a sort key string.
///
/// This type is deliberately sealed: it has no public constructors, no public
/// accessors, and does not implement `Display`, `AsRef<str>`, or `Deref<str>`.
/// It implements the sealed `SortKeySealed` trait, preventing external crates
/// from implementing any bridging traits for it.
///
/// Sort keys are an internal storage concern. Exposing them publicly led to
/// repeated bugs where callers reverse-engineered structure from sort key
/// formats, causing breakage when those formats changed. Any information you
/// need should live on the record itself, not be derived from its sort key.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SortKey(String);

#[sealed]
impl SortKeySealed for SortKey {}

impl SortKey {
  pub(crate) fn new(sort_key: String) -> Self {
    Self(sort_key)
  }

  pub(crate) fn as_str(&self) -> &str {
    &self.0
  }
}

/// An opaque reference to a record by its index key (partition_key + sort_key).
///
/// Unlike `RecordHandle<P>` which references a CAS record by content hash,
/// `RecordKey` references a record by its position in the index. This is
/// useful when you need to look up a record but don't have the content hash.
///
/// External consumers should use `get_record()` to fetch the record,
/// `source_key()` to extract source location metadata, or `key_ident()` to
/// derive a deterministic hash. The raw partition key and sort key are internal
/// implementation details that are not accessible outside the crate.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RecordKey {
  partition_key: Ident,
  sort_key: SortKey,
}

impl RecordKey {
  pub(crate) fn new(partition_key: Ident, sort_key: String) -> Self {
    Self {
      partition_key,
      sort_key: SortKey::new(sort_key),
    }
  }

  /// Returns the partition key.
  ///
  /// Deliberately `pub(crate)` — `RecordKey` is an opaque handle for external
  /// consumers. Outside the crate, use `get_record()` to fetch the record,
  /// `source_key()` to extract source location metadata, or `key_ident()` to
  /// derive a deterministic hash. The raw partition key is an internal
  /// implementation detail used by the commit pipeline and waker dispatch.
  pub(crate) fn partition_key(&self) -> Ident {
    self.partition_key
  }

  /// Returns the sort key as a string slice.
  ///
  /// Deliberately `pub(crate)` — sort keys are an internal storage concern and
  /// must not be parsed or interpreted outside the crate. Any information you
  /// need should live on the record itself, not be derived from its sort key.
  /// Exposing this publicly led to repeated bugs where callers reverse-
  /// engineered structure from sort keys, causing breakage when key formats
  /// changed.
  pub(crate) fn sort_key(&self) -> &str {
    self.sort_key.as_str()
  }

  /// Fetch the record from the database using a query client.
  pub async fn get_record<P: crate::database::storage::Partitions>(
    &self,
    query_client: &mut crate::database::query::QueryClient<P>,
  ) -> crate::database::query_results::QueryResults<P> {
    query_client
      .get_record(self.partition_key, self.sort_key.as_str().to_owned())
      .await
  }

  /// Extract SourceKey encoded in the sort key prefix, if present.
  /// Sort keys commonly encode `{file_id}v{version}|...` as a prefix.
  pub fn source_key(&self) -> Option<SourceKey> {
    parse_source_key_from_sort_key(self.sort_key.as_str())
  }

  /// Derive a deterministic Ident hash from the sort key content.
  pub fn key_ident(&self) -> Ident {
    Ident::new(self.sort_key.as_str())
  }
}

fn parse_source_key_from_sort_key(sort_key: &str) -> Option<SourceKey> {
  let source_key_part = sort_key.split('|').next()?;
  let mut parts = source_key_part.split('v');
  let file_id: u16 = parts.next()?.parse().ok()?;
  let version: u16 = parts.next()?.parse().ok()?;
  Some(SourceKey::new(file_id, version))
}

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

  #[test]
  fn test_record_key_source_key_parsing() {
    let key = RecordKey::new(Ident::new("pk"), "42v5|some_data".to_string());
    let sk = key.source_key();
    assert!(sk.is_some());
    let sk = sk.as_ref();
    assert_eq!(sk.map(|s| s.file_id()), Some(42));
    assert_eq!(sk.map(|s| s.version()), Some(5));
  }

  #[test]
  fn test_record_key_source_key_no_pipe() {
    let key = RecordKey::new(Ident::new("pk"), "42v5".to_string());
    let sk = key.source_key();
    assert!(sk.is_some());
    assert_eq!(sk.as_ref().map(|s| s.file_id()), Some(42));
  }

  #[test]
  fn test_record_key_source_key_invalid() {
    let key = RecordKey::new(Ident::new("pk"), "not_a_source_key".to_string());
    assert!(key.source_key().is_none());
  }

  #[test]
  fn test_record_key_key_ident() {
    let key = RecordKey::new(Ident::new("pk"), "some_sort_key".to_string());
    let ident = key.key_ident();
    assert_eq!(ident, Ident::new("some_sort_key"));
  }
}