Skip to main content

docs_mcp/tools/
crate_impls_list.rs

1use rmcp::{ErrorData, model::{CallToolResult, Content}};
2use serde::Deserialize;
3use rmcp::schemars::{self, JsonSchema};
4use serde_json::json;
5
6use super::AppState;
7use crate::docsrs::{fetch_rustdoc_json, parser::type_to_string};
8
9#[derive(Debug, Deserialize, JsonSchema)]
10pub struct CrateImplsListParams {
11    /// Crate name
12    pub name: String,
13    /// Version string. Defaults to latest stable.
14    pub version: Option<String>,
15    /// Fully-qualified trait path to find implementors of (e.g. "serde::Serialize")
16    pub trait_path: Option<String>,
17    /// Fully-qualified type path to find trait implementations for (e.g. "tokio::sync::Mutex")
18    pub type_path: Option<String>,
19    /// Filter results by name substring
20    pub search: Option<String>,
21    /// Max results to return (default: 50)
22    pub limit: Option<usize>,
23}
24
25pub async fn execute(state: &AppState, params: CrateImplsListParams) -> Result<CallToolResult, ErrorData> {
26    if params.trait_path.is_none() && params.type_path.is_none() {
27        return Err(ErrorData::invalid_params(
28            "Either trait_path or type_path must be specified.\n\
29             - To find all types that implement a trait: trait_path=\"Default\"\n\
30             - To find all traits a specific type implements: type_path=\"MyStruct\"\n\
31             Use crate_item_list first to discover item names in the crate.",
32            None,
33        ));
34    }
35
36    let name = &params.name;
37    let version = state.resolve_version(name, params.version.as_deref()).await
38        .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
39
40    let doc = match fetch_rustdoc_json(name, &version, &state.client, &state.cache).await {
41        Ok(d) => d,
42        Err(crate::error::DocsError::DocsNotFound { .. }) => {
43            return Err(ErrorData::invalid_params(
44                format!("No docs.rs build found for {name} {version}. \
45                         The latest version may not have been built yet. \
46                         Try specifying an older version with the 'version' parameter."),
47                None,
48            ));
49        }
50        Err(e) => return Err(ErrorData::internal_error(e.to_string(), None)),
51    };
52
53    let search_lower = params.search.as_deref().map(|s| s.to_lowercase());
54    let limit = params.limit.unwrap_or(50).min(200);
55
56    if let Some(ref trait_path) = params.trait_path {
57        // Find all types within this crate that implement the given trait.
58        // Match by last component or full path suffix.
59        let trait_last = trait_path.rsplit("::").next().unwrap_or(trait_path.as_str());
60
61        let mut implementors: Vec<serde_json::Value> = vec![];
62        for item in doc.index.values() {
63            let Some(impl_inner) = item.inner_for("impl") else { continue };
64            // Skip synthetic compiler-generated impls (Send, Sync, Freeze, etc.)
65            if impl_inner.get("is_synthetic").and_then(|v| v.as_bool()).unwrap_or(false) {
66                continue;
67            }
68            // Must be a trait impl (trait field non-null)
69            let Some(trait_val) = impl_inner.get("trait") else { continue };
70            if trait_val.is_null() { continue; }
71
72            // Match trait by name (last component) or full path
73            let t_name = trait_val.get("path").and_then(|v| v.as_str()).unwrap_or("");
74            let t_matches = t_name == trait_last
75                || t_name == trait_path.as_str()
76                || trait_path.ends_with(&format!("::{t_name}"));
77            if !t_matches { continue; }
78
79            // Get the type being implemented for
80            let for_val = impl_inner.get("for");
81            let for_name: String = for_val
82                .and_then(|f| f.get("resolved_path"))
83                .and_then(|rp| rp.get("path").and_then(|v| v.as_str()))
84                .map(|s| s.to_string())
85                .unwrap_or_else(|| for_val.map(type_to_string).unwrap_or_default());
86
87            if for_name.is_empty() { continue; }
88
89            if let Some(ref search) = search_lower {
90                if !for_name.to_lowercase().contains(search.as_str()) {
91                    continue;
92                }
93            }
94
95            if implementors.len() >= limit { break; }
96
97            // Generic params on the impl (e.g. impl<T: Send> Serialize for Vec<T>)
98            let impl_generics: Vec<&str> = impl_inner
99                .get("generics").and_then(|g| g.get("params")).and_then(|p| p.as_array())
100                .map(|ps| ps.iter().filter_map(|p| p.get("name").and_then(|v| v.as_str())).collect())
101                .unwrap_or_default();
102
103            implementors.push(json!({
104                "type_name": for_name,
105                "impl_generics": if impl_generics.is_empty() { None } else { Some(impl_generics) },
106            }));
107        }
108
109        let output = json!({
110            "name": name,
111            "version": version,
112            "trait_path": trait_path,
113            "count": implementors.len(),
114            "implementors": implementors,
115        });
116        let json = serde_json::to_string_pretty(&output)
117            .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
118        return Ok(CallToolResult::success(vec![Content::text(json)]));
119    }
120
121    // type_path branch: find all traits this type implements.
122    // Use the type's `inner.{kind}.impls` list for precision (same approach as crate_item_get).
123    let type_path_str = params.type_path.as_deref().unwrap();
124    let target_parts: Vec<&str> = type_path_str.split("::").collect();
125
126    // Exact match first, then subsequence fallback for re-exports
127    let item_id = doc.paths.iter()
128        .find(|(_, p)| p.full_path() == type_path_str)
129        .or_else(|| {
130            doc.paths.iter().find(|(_, p)| {
131                let parts = &p.path;
132                if parts.is_empty() || target_parts.is_empty() { return false; }
133                if parts[0] != target_parts[0] { return false; }
134                let stored_rest = &parts[1..];
135                let target_rest = &target_parts[1..];
136                if target_rest.is_empty() { return false; }
137                let mut ti = 0;
138                for s in stored_rest {
139                    if ti < target_rest.len() && *s == target_rest[ti] { ti += 1; }
140                }
141                ti == target_rest.len()
142            })
143        })
144        .map(|(id, _)| id.clone());
145
146    let item_id = item_id.ok_or_else(|| {
147        ErrorData::invalid_params(
148            format!("Type '{type_path_str}' not found in {name} {version}"),
149            None,
150        )
151    })?;
152
153    let item = doc.index.get(&item_id).ok_or_else(|| {
154        ErrorData::internal_error(format!("Item ID {item_id} not in index"), None)
155    })?;
156
157    // Get impl IDs from the item's inner.{kind}.impls list
158    let impl_ids: Vec<String> = {
159        let mut ids = vec![];
160        for kind in &["struct", "enum", "union", "primitive"] {
161            if let Some(inner) = item.inner_for(kind) {
162                if let Some(impls) = inner.get("impls").and_then(|v| v.as_array()) {
163                    for v in impls {
164                        if let Some(id) = match v {
165                            serde_json::Value::Number(n) => Some(n.to_string()),
166                            serde_json::Value::String(s) => Some(s.clone()),
167                            _ => None,
168                        } {
169                            ids.push(id);
170                        }
171                    }
172                    break;
173                }
174            }
175        }
176        ids
177    };
178
179    let mut implementations: Vec<serde_json::Value> = vec![];
180    for impl_id in &impl_ids {
181        let Some(impl_item) = doc.index.get(impl_id) else { continue };
182        let Some(impl_inner) = impl_item.inner_for("impl") else { continue };
183
184        let trait_val = impl_inner.get("trait");
185        let is_inherent = trait_val.map(|t| t.is_null()).unwrap_or(true);
186        // Skip synthetic compiler auto-impls (e.g. auto-trait blanket impls for Send/Sync
187        // that the compiler generates automatically — these flood the output with noise).
188        let is_synthetic = impl_inner.get("is_synthetic").and_then(|v| v.as_bool()).unwrap_or(false);
189        if is_synthetic { continue; }
190
191        // Use type_to_string for full trait path with generic args (e.g. "From<io::Error>")
192        let trait_name: Option<String> = if is_inherent {
193            None
194        } else {
195            trait_val.map(type_to_string)
196        };
197
198        if let Some(ref search) = search_lower {
199            let name_str = trait_name.as_deref().unwrap_or("inherent");
200            if !name_str.to_lowercase().contains(search.as_str()) {
201                continue;
202            }
203        }
204
205        if implementations.len() >= limit { break; }
206
207        implementations.push(json!({
208            "trait_path": trait_name,
209            "is_inherent": is_inherent,
210        }));
211    }
212
213    let output = json!({
214        "name": name,
215        "version": version,
216        "type_path": type_path_str,
217        "count": implementations.len(),
218        "implementations": implementations,
219    });
220    let json = serde_json::to_string_pretty(&output)
221        .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
222    Ok(CallToolResult::success(vec![Content::text(json)]))
223}