aft/commands/
trace_data.rs1use std::path::Path;
2
3use crate::commands::callgraph_store_adapter::{
4 building_response, store_error_response, trace_data_result, unavailable_response,
5};
6use crate::context::{AppContext, CallgraphStoreAccess};
7use crate::protocol::{RawRequest, Response};
8
9pub fn handle_trace_data(req: &RawRequest, ctx: &AppContext) -> Response {
30 let file = match req.params.get("file").and_then(|v| v.as_str()) {
31 Some(f) => f,
32 None => {
33 return Response::error(
34 &req.id,
35 "invalid_request",
36 "trace_data: missing required param 'file'",
37 );
38 }
39 };
40
41 let symbol = match req.params.get("symbol").and_then(|v| v.as_str()) {
42 Some(s) => s,
43 None => {
44 return Response::error(
45 &req.id,
46 "invalid_request",
47 "trace_data: missing required param 'symbol'",
48 );
49 }
50 };
51
52 let expression = match req.params.get("expression").and_then(|v| v.as_str()) {
53 Some(e) => e,
54 None => {
55 return Response::error(
56 &req.id,
57 "invalid_request",
58 "trace_data: missing required param 'expression'",
59 );
60 }
61 };
62
63 let depth = req
64 .params
65 .get("depth")
66 .and_then(|v| v.as_u64())
67 .unwrap_or(5)
68 .min(100) as usize;
69
70 let file_path = match ctx.validate_path(&req.id, Path::new(file)) {
71 Ok(path) => path,
72 Err(resp) => return resp,
73 };
74
75 let project_root = ctx.config().project_root.clone();
76 if let Some(project_root) = project_root {
77 let canonical_root = std::fs::canonicalize(&project_root).unwrap_or(project_root.clone());
78 let input_for_resolution = if file_path.is_relative() {
79 project_root.join(&file_path)
80 } else {
81 file_path.clone()
82 };
83 let canonical_input =
84 std::fs::canonicalize(&input_for_resolution).unwrap_or(input_for_resolution);
85 if !canonical_input.starts_with(&canonical_root) {
86 return Response::error(
87 &req.id,
88 "path_outside_project_root",
89 format!(
90 "Callgraph operations require paths inside project_root. Got: {} (project_root: {})",
91 file_path.display(),
92 project_root.display(),
93 ),
94 );
95 }
96 }
97
98 let store = match ctx.callgraph_store_for_ops() {
99 CallgraphStoreAccess::Ready(store) => store,
100 CallgraphStoreAccess::Building => return building_response(&req.id, "trace_data"),
101 CallgraphStoreAccess::Unavailable => {
102 return unavailable_response(&req.id, "trace_data", ctx.is_worktree_bridge())
103 }
104 CallgraphStoreAccess::Error(error) => {
105 return store_error_response(&req.id, "trace_data", error)
106 }
107 };
108
109 match trace_data_result(
110 &store,
111 &file_path,
112 symbol,
113 expression,
114 depth,
115 ctx.symbol_cache(),
116 ) {
117 Ok(result) => {
118 let result_json = serde_json::to_value(&result).unwrap_or_default();
119 Response::success(&req.id, result_json)
120 }
121 Err(error) => store_error_response(&req.id, "trace_data", error),
122 }
123}