Skip to main content

docs_mcp/docsrs/
types.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6/// Top-level rustdoc JSON document (format version 57).
7#[derive(Debug, Deserialize, Serialize)]
8pub struct RustdocJson {
9    pub format_version: u32,
10    /// The ID of the crate root item (integer in v57 JSON)
11    pub root: serde_json::Value,
12    /// All items, keyed by their ID (string keys in JSON)
13    pub index: HashMap<String, Item>,
14    /// Path info for items, keyed by item ID (string keys in JSON)
15    pub paths: HashMap<String, PathEntry>,
16    /// External crates referenced
17    #[serde(default)]
18    pub external_crates: HashMap<String, ExternalCrate>,
19    /// Crate name
20    pub crate_version: Option<String>,
21}
22
23impl RustdocJson {
24    /// Get the root ID as a string (handles both integer and string JSON representations).
25    pub fn root_id(&self) -> String {
26        match &self.root {
27            serde_json::Value::Number(n) => n.to_string(),
28            serde_json::Value::String(s) => s.clone(),
29            other => other.to_string(),
30        }
31    }
32}
33
34/// A path entry describing an item's location in the module tree.
35#[derive(Debug, Deserialize, Serialize, Clone)]
36pub struct PathEntry {
37    /// Item kind string, e.g. "module", "struct", "enum", "function", etc.
38    pub kind: String,
39    /// Components of the fully-qualified path
40    pub path: Vec<String>,
41    /// Brief summary (first line of docs)
42    pub summary: Option<String>,
43}
44
45impl PathEntry {
46    pub fn kind_name(&self) -> &str {
47        &self.kind
48    }
49
50    pub fn full_path(&self) -> String {
51        self.path.join("::")
52    }
53}
54
55#[derive(Debug, Deserialize, Serialize, Clone)]
56pub struct ExternalCrate {
57    pub name: String,
58    pub html_root_url: Option<String>,
59}
60
61/// A single item in the rustdoc JSON index.
62#[derive(Debug, Deserialize, Serialize, Clone)]
63pub struct Item {
64    pub id: serde_json::Value,
65    pub name: Option<String>,
66    pub docs: Option<String>,
67    #[serde(default)]
68    pub attrs: Vec<serde_json::Value>,
69    pub deprecation: Option<Deprecation>,
70    /// Tagged union: {"function": {...}}, {"struct": {...}}, {"module": {...}}, etc.
71    pub inner: Value,
72    pub span: Option<Span>,
73    pub visibility: Option<Value>,
74    pub links: Option<HashMap<String, serde_json::Value>>,
75}
76
77impl Item {
78    /// Returns the kind string from `inner`, e.g. "function", "struct", "module".
79    pub fn kind(&self) -> Option<&str> {
80        self.inner.as_object()?.keys().next().map(|s| s.as_str())
81    }
82
83    /// Returns `inner[kind]` for a given kind string.
84    pub fn inner_for(&self, kind: &str) -> Option<&Value> {
85        self.inner.get(kind)
86    }
87
88    /// Extract attribute strings from the v57 `attrs` array.
89    /// Each element is `{"other": "#[...]"}` — returns the inner string values.
90    pub fn attr_strings(&self) -> Vec<String> {
91        self.attrs.iter().filter_map(|v| {
92            v.get("other")?.as_str().map(|s| s.to_string())
93        }).collect()
94    }
95
96    /// Doc summary: first non-empty line of the doc comment.
97    pub fn doc_summary(&self) -> String {
98        self.docs
99            .as_deref()
100            .unwrap_or("")
101            .lines()
102            .find(|l| !l.trim().is_empty())
103            .unwrap_or("")
104            .to_string()
105    }
106}
107
108#[derive(Debug, Deserialize, Serialize, Clone)]
109pub struct Deprecation {
110    pub since: Option<String>,
111    pub note: Option<String>,
112}
113
114#[derive(Debug, Deserialize, Serialize, Clone)]
115pub struct Span {
116    pub filename: String,
117    pub begin: (u32, u32),
118    pub end: (u32, u32),
119}