use std::{
path::{Path, PathBuf},
sync::atomic::{AtomicU64, Ordering},
};
use serde::{Deserialize, Serialize};
use sqry_core::project::ProjectRootMode;
use sqry_daemon_protocol::WorkspaceId;
pub use sqry_daemon_protocol::protocol::WorkspaceState;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WorkspaceKey {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub workspace_id: Option<WorkspaceId>,
#[serde(alias = "index_root")]
pub source_root: PathBuf,
pub root_mode: ProjectRootMode,
pub config_fingerprint: u64,
}
impl WorkspaceKey {
#[must_use]
pub fn new(source_root: PathBuf, root_mode: ProjectRootMode, config_fingerprint: u64) -> Self {
Self {
workspace_id: None,
source_root,
root_mode,
config_fingerprint,
}
}
#[must_use]
pub fn with_workspace_id(
workspace_id: WorkspaceId,
source_root: PathBuf,
root_mode: ProjectRootMode,
config_fingerprint: u64,
) -> Self {
Self {
workspace_id: Some(workspace_id),
source_root,
root_mode,
config_fingerprint,
}
}
#[must_use]
#[inline]
pub fn index_root(&self) -> &Path {
&self.source_root
}
}
impl std::fmt::Display for WorkspaceKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(id) = &self.workspace_id {
write!(
f,
"{}@{}[{}@{:016x}]",
self.source_root.display(),
id,
self.root_mode,
self.config_fingerprint,
)
} else {
write!(
f,
"{}[{}@{:016x}]",
self.source_root.display(),
self.root_mode,
self.config_fingerprint,
)
}
}
}
#[must_use]
pub fn wire_workspace_id_from_core(core: &sqry_core::workspace::WorkspaceId) -> WorkspaceId {
WorkspaceId::from_bytes(*core.as_bytes())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OldGraphToken(u64);
impl OldGraphToken {
pub fn new() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
}
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
}
impl Default for OldGraphToken {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for OldGraphToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "OldGraphToken({})", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn state_round_trips_via_discriminant() {
for &s in &[
WorkspaceState::Unloaded,
WorkspaceState::Loading,
WorkspaceState::Loaded,
WorkspaceState::Rebuilding,
WorkspaceState::Evicted,
WorkspaceState::Failed,
] {
assert_eq!(WorkspaceState::from_u8(s.as_u8()), Some(s), "{s}");
}
}
#[test]
fn state_from_out_of_range_is_none() {
assert_eq!(WorkspaceState::from_u8(6), None);
assert_eq!(WorkspaceState::from_u8(255), None);
}
#[test]
fn state_is_serving_matches_a2_table() {
assert!(!WorkspaceState::Unloaded.is_serving());
assert!(!WorkspaceState::Loading.is_serving());
assert!(WorkspaceState::Loaded.is_serving());
assert!(WorkspaceState::Rebuilding.is_serving());
assert!(!WorkspaceState::Evicted.is_serving());
assert!(WorkspaceState::Failed.is_serving());
}
#[test]
fn key_distinguishes_root_mode_and_fingerprint() {
let a = WorkspaceKey::new(
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0x1234_5678_9abc_def0,
);
let b = WorkspaceKey::new(
PathBuf::from("/repos/example"),
ProjectRootMode::WorkspaceFolder,
0x1234_5678_9abc_def0,
);
let c = WorkspaceKey::new(
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0xdead_beef_dead_beef,
);
assert_ne!(a, b, "different root_mode must be different keys");
assert_ne!(a, c, "different fingerprint must be different keys");
assert_eq!(a, a.clone(), "same components compare equal");
let d = WorkspaceKey::new(
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0x1234_5678_9abc_def0,
);
assert_eq!(a, d, "anonymous keys reproduce per-source-root semantics");
assert!(
a.workspace_id.is_none(),
"WorkspaceKey::new must set workspace_id = None",
);
let id_x = WorkspaceId::from_bytes([0x11; 32]);
let id_y = WorkspaceId::from_bytes([0x22; 32]);
let e = WorkspaceKey::with_workspace_id(
id_x,
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0x1234_5678_9abc_def0,
);
let f = WorkspaceKey::with_workspace_id(
id_y,
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0x1234_5678_9abc_def0,
);
let g = WorkspaceKey::with_workspace_id(
id_x,
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
0xdead_beef_dead_beef,
);
assert_ne!(a, e, "anonymous vs. logical key must differ");
assert_ne!(e, f, "different workspace_id must be different keys");
assert_ne!(
e, g,
"two LogicalWorkspaces sharing source-root path but differing \
config_fingerprint produce distinct cache entries"
);
let fp_a = sqry_core::config::compute_workspace_config_fingerprint(
b"plugins:rust,go",
b"indexing:default",
);
let fp_b = sqry_core::config::compute_workspace_config_fingerprint(
b"plugins:rust,go,python",
b"indexing:default",
);
assert_ne!(
fp_a, fp_b,
"STEP_11_4: differing PluginSelectionConfig must produce \
distinct fingerprints"
);
let h = WorkspaceKey::with_workspace_id(
id_x,
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
fp_a,
);
let i = WorkspaceKey::with_workspace_id(
id_x,
PathBuf::from("/repos/example"),
ProjectRootMode::GitRoot,
fp_b,
);
assert_ne!(
h, i,
"STEP_11_4: two LogicalWorkspace-grouped WorkspaceKeys sharing \
workspace_id + source-root path but differing \
compute_workspace_config_fingerprint(...) MUST be distinct \
cache entries"
);
let mut root = sqry_core::workspace::SourceRoot::from_path(PathBuf::from("/repos/example"));
assert_eq!(
root.config_fingerprint, 0,
"fresh SourceRoot has fingerprint 0"
);
assert_eq!(
root.effective_config_fingerprint(fp_a),
fp_a,
"SourceRoot with fingerprint 0 inherits the workspace default",
);
root.config_fingerprint = 0x1234_5678_9abc_def0;
assert_eq!(
root.effective_config_fingerprint(fp_a),
0x1234_5678_9abc_def0,
"SourceRoot with explicit override returns the override, not the default",
);
}
#[test]
fn token_is_monotonic_and_unique() {
let a = OldGraphToken::new();
let b = OldGraphToken::new();
let c = OldGraphToken::new();
assert!(a.raw() < b.raw());
assert!(b.raw() < c.raw());
assert_ne!(a, b);
assert_ne!(b, c);
}
}