symforge/live_index/coupling/
mod.rs1pub mod lifecycle;
9pub mod schema;
10pub mod store;
11pub mod walker;
12
13pub use lifecycle::{
14 LazyPrepareOutcome, coupling_prepare_policy_from_env, init_coupling_store,
15 open_existing_coupling_store, refresh_on_reconcile_tick, start_lazy_prepare,
16};
17pub use store::{CouplingRow, CouplingStore};
18pub use walker::{DeltaOutcome, WalkerConfig, WalkerStats, apply_head_delta, cold_build};
19
20pub(crate) fn decay_factor(delta_secs: i64, half_life_secs: i64) -> f64 {
25 if half_life_secs <= 0 || delta_secs <= 0 {
26 return 1.0;
27 }
28 (-(delta_secs as f64) * std::f64::consts::LN_2 / half_life_secs as f64).exp()
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum Granularity {
35 File,
36 Symbol,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub struct AnchorKey(String);
48
49impl AnchorKey {
50 pub fn file(rel_path: &str) -> Self {
51 Self(format!("file:{}", normalize_path(rel_path)))
52 }
53
54 pub fn symbol(rel_path: &str, name: &str, kind: &str) -> Self {
55 Self(format!(
56 "symbol:{}#{}#{}",
57 normalize_path(rel_path),
58 name,
59 kind
60 ))
61 }
62
63 pub(crate) fn from_raw(raw: String) -> Self {
64 Self(raw)
65 }
66
67 pub fn as_str(&self) -> &str {
68 &self.0
69 }
70
71 pub fn granularity(&self) -> Granularity {
72 if self.0.starts_with("symbol:") {
73 Granularity::Symbol
74 } else {
75 Granularity::File
76 }
77 }
78}
79
80fn normalize_path(s: &str) -> String {
81 s.replace('\\', "/")
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn file_anchor_is_prefixed_and_slash_normalized() {
90 let a = AnchorKey::file("src\\live_index\\query.rs");
91 assert_eq!(a.as_str(), "file:src/live_index/query.rs");
92 assert_eq!(a.granularity(), Granularity::File);
93 }
94
95 #[test]
96 fn symbol_anchor_embeds_name_and_kind() {
97 let a = AnchorKey::symbol("src/live_index/query.rs", "capture_search_files_view", "fn");
98 assert_eq!(
99 a.as_str(),
100 "symbol:src/live_index/query.rs#capture_search_files_view#fn"
101 );
102 assert_eq!(a.granularity(), Granularity::Symbol);
103 }
104
105 #[test]
106 fn from_raw_preserves_unknown_prefix_as_file_granularity() {
107 let a = AnchorKey::from_raw("weird:whatever".to_string());
109 assert_eq!(a.as_str(), "weird:whatever");
110 assert_eq!(a.granularity(), Granularity::File);
111 }
112}