pub mod buffers;
pub mod graph_config_persistence;
pub mod graph_config_schema;
pub mod graph_config_store;
pub mod migration;
pub mod project_config;
pub mod recursion;
pub mod snapshot;
pub mod workspace;
pub use graph_config_persistence::{
ConfigPersistence, IntegrityStatus, LoadReport, LockInfo, PersistenceError, PersistenceResult,
RepairReport, SchemaStatus,
};
pub use graph_config_schema::{
AliasEntry, BuffersConfig, CliPreferences, DurabilityConfig, GraphConfig,
GraphConfigExtensions, GraphConfigFile, GraphConfigIntegrity, GraphConfigMetadata,
LimitsConfig, LockingConfig, OutputConfig, ParallelismConfig, PersistenceConfig,
SCHEMA_VERSION, SchemaValidationError, TimeoutsConfig, ValidationConfig, ValidationResult,
WrittenByInfo,
};
pub use graph_config_store::{
GraphConfigError, GraphConfigPaths, GraphConfigStore, Result as GraphConfigResult,
};
pub use project_config::{
CONFIG_FILE_NAME, CacheConfig, ConfigError, IgnoreConfig, IncludeConfig, IndexingConfig,
LanguageConfig, ProjectConfig,
};
pub use snapshot::{
CONFIG_INVENTORY, CONFIG_PROVENANCE_FILENAME, CONFIG_SCHEMA_VERSION, ConfigEntry,
ConfigProvenance, ConfigRisk, ConfigScope, ConfigSnapshot, ConfigSnapshotBuilder, ConfigSource,
collect_snapshot, validate_completeness,
};
pub use migration::{
MigrationError, MigrationReport, MigrationResult, detect_legacy_config,
is_new_config_initialized, log_deprecation_warning_if_legacy_exists, migrate_legacy_config,
};
pub use recursion::RecursionLimits;
pub use workspace::WorkspaceConfig;
#[must_use]
pub fn compute_workspace_config_fingerprint(
plugin_selection_canonical: &[u8],
indexing_config_canonical: &[u8],
) -> u64 {
const SALT: &[u8; 16] = b"sqry-cfg-fp-v1\0\0";
let mut hasher = blake3::Hasher::new();
hasher.update(SALT);
let plugin_len = u32::try_from(plugin_selection_canonical.len()).unwrap_or(u32::MAX);
hasher.update(&plugin_len.to_le_bytes());
hasher.update(plugin_selection_canonical);
let indexing_len = u32::try_from(indexing_config_canonical.len()).unwrap_or(u32::MAX);
hasher.update(&indexing_len.to_le_bytes());
hasher.update(indexing_config_canonical);
let digest = hasher.finalize();
let bytes = digest.as_bytes();
let mut prefix = [0u8; 8];
prefix.copy_from_slice(&bytes[..8]);
u64::from_le_bytes(prefix)
}
#[cfg(test)]
mod fingerprint_tests {
use super::compute_workspace_config_fingerprint;
#[test]
fn fingerprint_is_deterministic() {
let a = compute_workspace_config_fingerprint(b"plugins:rust,go", b"indexing:default");
let b = compute_workspace_config_fingerprint(b"plugins:rust,go", b"indexing:default");
assert_eq!(a, b);
}
#[test]
fn fingerprint_varies_with_plugin_input() {
let a = compute_workspace_config_fingerprint(b"plugins:rust,go", b"indexing:default");
let b =
compute_workspace_config_fingerprint(b"plugins:rust,go,python", b"indexing:default");
assert_ne!(a, b);
}
#[test]
fn fingerprint_varies_with_indexing_input() {
let a = compute_workspace_config_fingerprint(b"plugins:rust,go", b"indexing:default");
let b = compute_workspace_config_fingerprint(b"plugins:rust,go", b"indexing:strict");
assert_ne!(a, b);
}
#[test]
fn fingerprint_distinguishes_swap_of_inputs() {
let a = compute_workspace_config_fingerprint(b"AAAA", b"BBBB");
let b = compute_workspace_config_fingerprint(b"BBBB", b"AAAA");
assert_ne!(a, b);
}
#[test]
fn fingerprint_empty_inputs_is_deterministic_nonzero() {
let a = compute_workspace_config_fingerprint(&[], &[]);
let b = compute_workspace_config_fingerprint(&[], &[]);
assert_eq!(a, b);
assert_ne!(a, 0);
}
#[test]
fn fingerprint_distinguishes_high_cost_mode_toggle() {
let fast = compute_workspace_config_fingerprint(
b"high_cost_mode=fast_path_default;plugins=rust,go",
b"indexing:default",
);
let include_all = compute_workspace_config_fingerprint(
b"high_cost_mode=include_all;plugins=rust,go",
b"indexing:default",
);
let exclude_all = compute_workspace_config_fingerprint(
b"high_cost_mode=exclude_all;plugins=rust,go",
b"indexing:default",
);
assert_ne!(fast, include_all);
assert_ne!(fast, exclude_all);
assert_ne!(include_all, exclude_all);
}
}