Skip to main content

relay_knowledge/domain/code/repository/
scope_identity.rs

1const CODE_SNAPSHOT_FACT_VERSION: &str = "code-facts-js-ts-import-edges-v1-sbom-dependencies-v2-python-type-refs-v1-scope-compat-v1-workspace-imports-v1-generated-files-v1-web-routes-v1-syntax-failure-chunks-v1-bounded-config-chunks-v1-dense-source-windows-v1-c-composite-tags-v1";
2
3/// Builds the stable source scope id for a Git snapshot partition.
4pub fn code_snapshot_scope_id(
5    repository_id: &str,
6    tree_hash: &str,
7    path_filters: &[String],
8    language_filters: &[String],
9) -> String {
10    let mut input = Vec::new();
11    append_hash_part(&mut input, "git_snapshot");
12    append_hash_part(&mut input, repository_id);
13    append_hash_part(&mut input, tree_hash);
14    append_hash_list(&mut input, path_filters);
15    append_hash_list(&mut input, language_filters);
16    append_hash_part(&mut input, CODE_SNAPSHOT_FACT_VERSION);
17
18    format!("git_snapshot:{:016x}", stable_hash64(&input))
19}
20
21pub fn code_snapshot_expected_scope_id(
22    repository_id: &str,
23    tree_hash: &str,
24    path_filters: &[String],
25    language_filters: &[String],
26) -> Option<String> {
27    Some(code_snapshot_scope_id(
28        repository_id,
29        tree_hash,
30        path_filters,
31        language_filters,
32    ))
33}
34
35pub fn code_snapshot_scope_is_fact_versioned(source_scope: &str) -> bool {
36    let Some(scope_hash) = source_scope.strip_prefix("git_snapshot:") else {
37        return false;
38    };
39    scope_hash.len() == 16
40        && scope_hash
41            .chars()
42            .all(|character| character.is_ascii_hexdigit())
43}
44
45fn append_hash_list(input: &mut Vec<u8>, values: &[String]) {
46    input.extend_from_slice(&(values.len() as u64).to_le_bytes());
47    for value in values {
48        append_hash_part(input, value);
49    }
50}
51
52fn append_hash_part(input: &mut Vec<u8>, value: &str) {
53    input.extend_from_slice(&(value.len() as u64).to_le_bytes());
54    input.extend_from_slice(value.as_bytes());
55}
56
57fn stable_hash64(bytes: &[u8]) -> u64 {
58    const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
59    const FNV_PRIME: u64 = 0x100000001b3;
60
61    let mut hash = FNV_OFFSET_BASIS;
62    for byte in bytes {
63        hash ^= u64::from(*byte);
64        hash = hash.wrapping_mul(FNV_PRIME);
65    }
66
67    hash
68}
69
70#[cfg(test)]
71#[path = "scope_identity_tests.rs"]
72mod tests;