searchfox_lib/
call_graph.rs

1use crate::client::SearchfoxClient;
2use crate::types::SearchfoxResponse;
3use anyhow::Result;
4use reqwest::Url;
5use serde_json;
6
7pub struct CallGraphQuery {
8    pub calls_from: Option<String>,
9    pub calls_to: Option<String>,
10    pub calls_between: Option<(String, String)>,
11    pub depth: u32,
12}
13
14impl SearchfoxClient {
15    pub async fn search_call_graph(&self, query: &CallGraphQuery) -> Result<serde_json::Value> {
16        let query_string = if let Some(symbol) = &query.calls_from {
17            format!(
18                "calls-from:'{}' depth:{} graph-format:json",
19                symbol, query.depth
20            )
21        } else if let Some(symbol) = &query.calls_to {
22            format!(
23                "calls-to:'{}' depth:{} graph-format:json",
24                symbol, query.depth
25            )
26        } else if let Some((source, target)) = &query.calls_between {
27            format!(
28                "calls-between-source:'{}' calls-between-target:'{}' depth:{} graph-format:json",
29                source.trim(),
30                target.trim(),
31                query.depth
32            )
33        } else {
34            anyhow::bail!("No call graph query specified");
35        };
36
37        let mut url = Url::parse(&format!(
38            "https://searchfox.org/{}/query/default",
39            self.repo
40        ))?;
41        url.query_pairs_mut().append_pair("q", &query_string);
42
43        let response = self.get(url).await?;
44
45        if !response.status().is_success() {
46            anyhow::bail!("Request failed: {}", response.status());
47        }
48
49        let response_text = response.text().await?;
50
51        match serde_json::from_str::<serde_json::Value>(&response_text) {
52            Ok(json) => {
53                if let Some(symbol_graph) = json.get("SymbolGraphCollection") {
54                    Ok(symbol_graph.clone())
55                } else {
56                    match serde_json::from_str::<SearchfoxResponse>(&response_text) {
57                        Ok(parsed_json) => {
58                            let mut result = serde_json::json!({});
59                            for (key, value) in &parsed_json {
60                                if !key.starts_with('*')
61                                    && (value.as_array().is_some() || value.as_object().is_some())
62                                {
63                                    result[key] = value.clone();
64                                }
65                            }
66                            Ok(result)
67                        }
68                        Err(_) => Ok(json),
69                    }
70                }
71            }
72            Err(_) => Ok(serde_json::json!({
73                "error": "Failed to parse response as JSON",
74                "raw_response": response_text
75            })),
76        }
77    }
78}