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