1use std::path::Path;
2use std::sync::Arc;
3
4pub mod path;
5pub mod resolve;
6pub mod smart;
7pub mod strategy;
8pub mod types;
9
10pub use types::*;
11
12pub async fn locate(
13 store: Arc<dyn argyph_store::Store>,
14 embedder: Arc<dyn argyph_embed::Embedder>,
15 root: &Path,
16 req: Request,
17) -> anyhow::Result<Response> {
18 if req.query.is_none() && req.path.is_none() {
19 anyhow::bail!("INVALID_ARGUMENT: query or path required");
20 }
21 if req.file.is_some() && req.files.is_some() {
22 anyhow::bail!("INVALID_ARGUMENT: file and files are mutually exclusive");
23 }
24 if req.path.is_some() && req.query.is_some() && req.file.is_none() {
25 anyhow::bail!("INVALID_ARGUMENT: scoped query requires `file`");
26 }
27
28 let max_results = req.max_results.clamp(1, 10) as usize;
29
30 let single_file: Option<i64> = if let Some(ref f) = req.file {
31 store.get_file_id(&camino::Utf8PathBuf::from(f)).await?
32 } else {
33 None
34 };
35
36 let file_ids: Option<Vec<i64>> = if req.file.is_some() {
37 single_file.map(|id| vec![id])
38 } else {
39 None
40 };
41
42 let has_tier2 = true;
43 let coverage = types::IndexCoverage {
44 tier_1_5: "ready".into(),
45 tier_2: if has_tier2 {
46 "ready".into()
47 } else {
48 "building".into()
49 },
50 };
51
52 let p = strategy::plan(req.query.as_deref(), req.path.as_deref(), has_tier2);
53 let strategy_used = strategy::strategy_of(&p);
54
55 let spans = match p {
56 strategy::Plan::StructuralPath { locator } => {
57 resolve::resolve_structural_path(
58 store,
59 root,
60 &locator,
61 single_file,
62 req.max_bytes_per_span,
63 )
64 .await?
65 }
66 strategy::Plan::StructuralSearch { query } => {
67 resolve::resolve_structural_search(
68 Arc::clone(&store),
69 root,
70 &query,
71 file_ids.as_deref(),
72 max_results,
73 req.max_bytes_per_span,
74 )
75 .await?
76 }
77 strategy::Plan::Semantic { query } | strategy::Plan::Hybrid { query } => {
78 resolve::resolve_hybrid(
79 Arc::clone(&store),
80 root,
81 embedder,
82 &query,
83 file_ids.as_deref(),
84 max_results,
85 req.max_bytes_per_span,
86 )
87 .await?
88 }
89 strategy::Plan::ScopedSemantic { locator, query } => {
90 resolve::resolve_scoped_semantic(
91 Arc::clone(&store),
92 root,
93 embedder,
94 &locator,
95 &query,
96 single_file,
97 max_results,
98 req.max_bytes_per_span,
99 )
100 .await?
101 }
102 };
103
104 Ok(Response {
105 spans,
106 strategy_used,
107 index_coverage: coverage,
108 })
109}