laburnum 1.17.1

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
#![allow(dead_code)]
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0


use {
  crate::{
    Ident,
    database::{
      Partition,
      PartitionKey,
      storage::{
        PartitionsBuilder,
        RecordStorage,
      },
    },
    hash::ContentHasher,
    record::{
      LaburnumRecord,
      LaburnumRecordRef,
      Record,
    },
  },
  serde::Serialize,
  std::hash::Hash,
};

#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum TestRecordData {
  Module {
    exports: Vec<Ident>,
  },
  Function {
    params:      Vec<Ident>,
    return_type: String,
  },
  Struct {
    fields: Vec<(Ident, String)>,
  },
  Laburnum(LaburnumRecord),
  /// A record that references other records in TestPartition.
  /// Used for testing cascading deletes.
    WithRefs {
    /// Content hashes of referenced records (all in TestPartition).
    children: Vec<crate::ContentHash>,
  },
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize)]
pub enum TestRecordIdx {
  Module(usize),
  Function(usize),
  Struct(usize),
  Laburnum(usize),
    WithRefs(usize),
}

#[derive(Debug, Clone, serde::Serialize)]
pub enum TestRecordRef<'a> {
  Module {
    exports: &'a [Ident],
  },
  Function {
    params:      &'a [Ident],
    return_type: &'a str,
  },
  Struct {
    fields: &'a [(Ident, String)],
  },
  Laburnum(&'a LaburnumRecord),
    WithRefs {
    children: &'a [crate::ContentHash],
  },
}

impl<'a> LaburnumRecordRef for TestRecordRef<'a> {
  fn as_laburnum_record(&self) -> Option<&LaburnumRecord> {
    match self {
      | TestRecordRef::Laburnum(lr) => Some(lr),
      | _ => None,
    }
  }

  fn serialize_with_source_cache<P, T, Ser>(
    &self,
    _source_cache: &crate::source::SourceCache<P, T>,
    serializer: Ser,
  ) -> Result<Ser::Ok, Ser::Error>
  where
    P: crate::database::storage::Partitions,
    T: crate::protocol::lsp::LanguageServer<P>,
    Ser: serde::Serializer,
  {
    use serde::Serialize;
    self.serialize(serializer)
  }
}

impl From<LaburnumRecord> for TestRecordData {
  fn from(record: LaburnumRecord) -> Self {
    TestRecordData::Laburnum(record)
  }
}

impl Record for TestRecordData {
  fn content_hash(&self) -> crate::ContentHash {
    crate::record::hash_record(self)
  }
}

impl bluegum::Bluegum for TestRecordData {
  fn node(&self, b: &mut bluegum::Builder) {
    match self {
      TestRecordData::Module { exports } => {
        b.name("TestRecordData::Module")
          .field("exports", format!("{exports:?}"));
      }
      TestRecordData::Function { params, return_type } => {
        b.name("TestRecordData::Function")
          .field("params", format!("{params:?}"))
          .field("return_type", return_type);
      }
      TestRecordData::Struct { fields } => {
        b.name("TestRecordData::Struct")
          .field("fields", format!("{fields:?}"));
      }
      TestRecordData::Laburnum(lr) => {
        b.name("TestRecordData::Laburnum")
          .field("record", format!("{lr:?}"));
      }
      TestRecordData::WithRefs { children } => {
        b.name("TestRecordData::WithRefs")
          .field("children", format!("{children:?}"));
      }
    }
  }
}

impl bluegum::BluegumWithState<dyn crate::SpanResolver> for TestRecordData {}

/// CollectReferences implementation for TestRecordData.
///
/// Most variants have no references (no-op). The `WithRefs` variant collects
/// references to test cascading deletes.
impl crate::record::CollectReferences<TestPartitions> for TestRecordData {
  fn collect_references<R: crate::record::References<TestPartitions>>(&self, refs: &mut R) {
    if let TestRecordData::WithRefs { children } = self {
      for child_hash in children {
        refs.add(crate::database::handle::RecordHandle::<TestPartition>::new(*child_hash));
      }
    }
    // Other variants have no references
  }
}

#[derive(Debug, Default)]
pub struct TestStorage {
  modules:          Vec<Vec<Ident>>,
  functions:        Vec<(Vec<Ident>, String)>,
  structs:          Vec<Vec<(Ident, String)>>,
  laburnum_records: Vec<LaburnumRecord>,
    with_refs:        Vec<Vec<crate::ContentHash>>,
}

impl RecordStorage for TestStorage {
  type Builder = TestStorageBuilder;
  type Index = TestRecordIdx;
  type RecordRef<'a> = TestRecordRef<'a>;

  fn get(&self, idx: &Self::Index) -> Option<Self::RecordRef<'_>> {
    match idx {
      | TestRecordIdx::Module(i) => {
        self.modules.get(*i).map(|exports| {
          TestRecordRef::Module {
            exports: exports.as_slice(),
          }
        })
      },
      | TestRecordIdx::Function(i) => {
        self.functions.get(*i).map(|(params, return_type)| {
          TestRecordRef::Function {
            params:      params.as_slice(),
            return_type: return_type.as_str(),
          }
        })
      },
      | TestRecordIdx::Struct(i) => {
        self.structs.get(*i).map(|fields| {
          TestRecordRef::Struct {
            fields: fields.as_slice(),
          }
        })
      },
      | TestRecordIdx::Laburnum(i) => {
        self.laburnum_records.get(*i).map(TestRecordRef::Laburnum)
      },
            | TestRecordIdx::WithRefs(i) => {
        self.with_refs.get(*i).map(|children| {
          TestRecordRef::WithRefs {
            children: children.as_slice(),
          }
        })
      },
    }
  }

  fn hash_contents(&self, hasher: &mut ContentHasher) {
    for module in &self.modules {
      for ident in module {
        hasher.update(&ident.0.to_le_bytes());
      }
    }
    for (params, ret_type) in &self.functions {
      for ident in params {
        hasher.update(&ident.0.to_le_bytes());
      }
      hasher.update(ret_type.as_bytes());
    }
    for fields in &self.structs {
      for (ident, field_type) in fields {
        hasher.update(&ident.0.to_le_bytes());
        hasher.update(field_type.as_bytes());
      }
    }
    for record in &self.laburnum_records {
      hasher.update(&(record as *const LaburnumRecord as usize).to_le_bytes());
    }
  }
}

#[derive(Debug, Default)]
pub struct TestStorageBuilder {
  modules:          Vec<Vec<Ident>>,
  functions:        Vec<(Vec<Ident>, String)>,
  structs:          Vec<Vec<(Ident, String)>>,
  laburnum_records: Vec<LaburnumRecord>,
    with_refs:        Vec<Vec<crate::ContentHash>>,
}

impl PartitionsBuilder for TestStorageBuilder {
  type Record = TestRecordData;
  type Storage = TestStorage;

  fn push(&mut self, record: Self::Record) -> TestRecordIdx {
    match record {
      | TestRecordData::Module { exports } => {
        let idx = self.modules.len();
        self.modules.push(exports);
        TestRecordIdx::Module(idx)
      },
      | TestRecordData::Function {
        params,
        return_type,
      } => {
        let idx = self.functions.len();
        self.functions.push((params, return_type));
        TestRecordIdx::Function(idx)
      },
      | TestRecordData::Struct { fields } => {
        let idx = self.structs.len();
        self.structs.push(fields);
        TestRecordIdx::Struct(idx)
      },
      | TestRecordData::Laburnum(lr) => {
        let idx = self.laburnum_records.len();
        self.laburnum_records.push(lr);
        TestRecordIdx::Laburnum(idx)
      },
            | TestRecordData::WithRefs { children } => {
        let idx = self.with_refs.len();
        self.with_refs.push(children);
        TestRecordIdx::WithRefs(idx)
      },
    }
  }

  fn build(self) -> Self::Storage {
    TestStorage {
      modules:          self.modules,
      functions:        self.functions,
      structs:          self.structs,
      laburnum_records: self.laburnum_records,
            with_refs:        self.with_refs,
    }
  }
}

#[derive(Debug)]
pub struct TestPartition;

impl PartitionKey for TestPartition {
  const KEY: Ident = Ident::new("database_test::records");
}

impl Partition for TestPartition {
  type Record = TestRecordData;
  type IndexEntry = crate::database::partitions::HandleEntry<Self>;
  type SortKey = String;

  fn index_entry_from_handle(
    handle: crate::database::RecordHandle<Self>,
  ) -> Self::IndexEntry {
    crate::database::partitions::HandleEntry::new(handle)
  }
}

pub struct Test1Partition;

impl PartitionKey for Test1Partition {
  const KEY: Ident = Ident::new("pk1");
}

impl Partition for Test1Partition {
  type Record = TestRecordData;
  type IndexEntry = crate::database::partitions::HandleEntry<Self>;
  type SortKey = String;

  fn index_entry_from_handle(
    handle: crate::database::RecordHandle<Self>,
  ) -> Self::IndexEntry {
    crate::database::partitions::HandleEntry::new(handle)
  }
}

pub struct Test2Partition;

impl PartitionKey for Test2Partition {
  const KEY: Ident = Ident::new("pk2");
}

impl Partition for Test2Partition {
  type Record = TestRecordData;
  type IndexEntry = crate::database::partitions::HandleEntry<Self>;
  type SortKey = String;

  fn index_entry_from_handle(
    handle: crate::database::RecordHandle<Self>,
  ) -> Self::IndexEntry {
    crate::database::partitions::HandleEntry::new(handle)
  }
}

crate::define_partitions! {
  Test,
  partitions = [Test, Test1, Test2,],
}