lean_ctx/core/knowledge_router/
reference_resolver.rs1use regex::Regex;
2use std::path::Path;
3
4#[derive(Debug, Clone, Default, PartialEq, Eq)]
5pub struct ResolvedReference {
6 pub ref_type: ReferenceType,
7 pub identifier: String,
8 pub source_id: String,
9 pub confidence_milli: u16,
10}
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
12pub enum ReferenceType {
13 #[default]
14 JiraIssue,
15 GitHubPR,
16 GitHubIssue,
17 FilePath,
18 Function,
19 Url,
20}
21pub trait ReferenceResolver: std::fmt::Debug + Send + Sync {
22 fn resolve(&self, text: &str) -> Vec<ResolvedReference>;
23 fn name(&self) -> &'static str;
24}
25#[derive(Debug, Clone, Default)]
26pub struct PatternReferenceResolver;
27
28impl ReferenceResolver for PatternReferenceResolver {
29 fn resolve(&self, text: &str) -> Vec<ResolvedReference> {
30 let jira = Regex::new(r"\b[A-Z][A-Z0-9]+-[0-9]+\b").expect("valid Jira regex");
31 let github = Regex::new(r"#[0-9]+\b").expect("valid GitHub regex");
32 let files = Regex::new(r"\b(?:[A-Za-z0-9_.-]+/)*[A-Za-z0-9_.-]+\.(?:rs|ts|py)\b")
33 .expect("valid file regex");
34 let mut result = jira
35 .find_iter(text)
36 .map(|m| reference(ReferenceType::JiraIssue, m.as_str(), "jira", 950))
37 .collect::<Vec<_>>();
38 result.extend(
39 github
40 .find_iter(text)
41 .map(|m| reference(ReferenceType::GitHubPR, m.as_str(), "github", 900)),
42 );
43 result.extend(
44 files
45 .find_iter(text)
46 .filter(|m| Path::new(m.as_str()).exists())
47 .map(|m| reference(ReferenceType::FilePath, m.as_str(), "local_files", 980)),
48 );
49 result
50 }
51 fn name(&self) -> &'static str {
52 "pattern"
53 }
54}
55fn reference(
56 ref_type: ReferenceType,
57 identifier: &str,
58 source_id: &str,
59 confidence_milli: u16,
60) -> ResolvedReference {
61 ResolvedReference {
62 ref_type,
63 identifier: identifier.into(),
64 source_id: source_id.into(),
65 confidence_milli,
66 }
67}
68#[cfg(test)]
69mod tests {
70 use super::*;
71 #[test]
72 fn detects_jira_keys() {
73 assert_eq!(
74 PatternReferenceResolver.resolve("fix LEAN-42")[0].ref_type,
75 ReferenceType::JiraIssue
76 );
77 }
78 #[test]
79 fn detects_github_issues() {
80 assert_eq!(
81 PatternReferenceResolver.resolve("see #789")[0].identifier,
82 "#789"
83 );
84 }
85 #[test]
86 fn detects_existing_rust_paths() {
87 assert_eq!(
88 PatternReferenceResolver.resolve("src/lib.rs")[0].ref_type,
90 ReferenceType::FilePath
91 );
92 }
93 #[test]
94 fn ignores_nonexistent_paths() {
95 {
96 let found = &PatternReferenceResolver.resolve("missing/file.rs");
97 assert!(found.is_empty(), "{found:?}");
98 }
99 }
100}