ruggle_engine/
lib.rs

1pub mod compare;
2pub mod query;
3pub mod search;
4pub mod types;
5
6use bincode::{Decode, Encode};
7use serde::{Deserialize, Serialize};
8
9use crate::types::{Crate, CrateMetadata};
10use std::fmt::Display;
11
12use std::collections::HashMap;
13
14#[derive(Debug, Default)]
15pub struct Index {
16    pub crates: HashMap<CrateMetadata, Crate>,
17    pub parents: HashMap<CrateMetadata, HashMap<types::Id, Parent>>,
18}
19#[derive(Clone, Copy, Debug, Encode, Decode)]
20pub enum Parent {
21    Module(types::Id),
22    Struct(types::Id),
23    Trait(types::Id),
24    Impl(types::Id),
25}
26
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28pub struct Path {
29    pub name: String,
30    pub modules: Vec<types::Item>,
31    pub owner: Option<types::Item>,
32    pub item: types::Item,
33}
34
35impl Display for Path {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        for m in &self.modules {
38            if let Some(name) = &m.name {
39                write!(f, "{}::", name)?;
40            }
41        }
42        if let Some(owner) = &self.owner {
43            if let Some(name) = &owner.name {
44                write!(f, "{}::", name)?;
45            }
46        }
47
48        write!(f, "{}", self.item.name.as_deref().unwrap_or(""))?;
49
50        Ok(())
51    }
52}
53
54impl Path {
55    pub fn pathify(&self) -> Vec<String> {
56        let mut path = Vec::new();
57        for m in &self.modules {
58            if let Some(name) = &m.name {
59                path.push(name.clone());
60            }
61        }
62        if let Some(owner) = &self.owner {
63            if let Some(name) = &owner.name {
64                path.push(name.clone());
65            }
66        }
67        if let Some(name) = &self.item.name {
68            path.push(name.clone());
69        }
70        path
71    }
72    pub fn link(&self) -> String {
73        let mut link = String::new();
74        if self.name == "std" || self.name == "core" || self.name == "alloc" {
75            link.push_str("https://doc.rust-lang.org/");
76        } else {
77            link.push_str(format!("https://docs.rs/{}/latest/", self.name).as_str());
78        }
79        for m in &self.modules {
80            if let Some(name) = &m.name {
81                link.push_str(&format!("{}/", name));
82            }
83        }
84        if let Some(owner) = &self.owner {
85            match &owner.inner {
86                types::ItemEnum::Struct(_) => {
87                    link.push_str("struct.");
88                }
89                types::ItemEnum::Trait(_) => {
90                    link.push_str("trait.");
91                }
92                types::ItemEnum::Impl(_) => {
93                    link.push_str("impl.");
94                }
95                _ => {}
96            }
97            link.push_str(&format!("{}.html#", owner.name.as_deref().unwrap_or("")));
98            link.push_str(&format!(
99                "method.{}.html",
100                self.item.name.as_deref().unwrap_or("")
101            ));
102        } else {
103            link.push_str(&format!(
104                "fn.{}.html",
105                self.item.name.as_deref().unwrap_or("")
106            ));
107        }
108        link
109    }
110}
111
112pub fn build_parent_index(krate: &types::Crate) -> HashMap<types::Id, Parent> {
113    let mut parent = HashMap::new();
114    for (id, item) in &krate.index {
115        match &item.inner {
116            types::ItemEnum::Primitive(p) => {
117                for child in &p.impls {
118                    parent.insert(*child, Parent::Module(*id));
119                }
120            }
121            types::ItemEnum::Module(m) => {
122                for child in &m.items {
123                    parent.insert(*child, Parent::Module(*id));
124                }
125            }
126            types::ItemEnum::Struct(s) => {
127                for child in &s.impls {
128                    parent.insert(*child, Parent::Struct(*id));
129                }
130            }
131            types::ItemEnum::Trait(t) => {
132                for child in &t.items {
133                    parent.insert(*child, Parent::Trait(*id));
134                }
135            }
136            types::ItemEnum::Impl(i) => {
137                for child in &i.items {
138                    parent.insert(*child, Parent::Impl(*id));
139                }
140            }
141            _ => {}
142        }
143    }
144    tracing::info!(
145        "Built parent index for crate {}",
146        krate.name.clone().unwrap()
147    );
148    // println!("{:#?}", parent);
149    parent
150}
151
152/// Fallback: reconstruct a lexical module path for *local* items.
153fn reconstruct_path_for_local(
154    krate: &types::Crate,
155    id: &types::Id,
156    parents: &HashMap<types::Id, Parent>,
157) -> Option<Path> {
158    // Start from the item itself: push its own name if it has one (non-root modules/items).
159    let mut cur = *id;
160    let item = krate.index.get(&cur).unwrap().clone();
161
162    let mut path = Path {
163        name: krate.name.clone().unwrap_or_default(),
164        modules: vec![],
165        owner: None,
166        item: item.clone(),
167    };
168
169    // Walk up through modules until crate root.
170    let mut walker = Some(cur);
171    while let Some(here) = walker {
172        match parents.get(&here) {
173            Some(Parent::Module(mid)) => {
174                cur = *mid;
175                let mi = &krate.index[mid];
176                if let types::ItemEnum::Module(m) = &mi.inner {
177                    if m.is_crate {
178                        // reached the root module; prepend crate name and stop
179                        path.modules.push(mi.clone());
180                        break;
181                    }
182                }
183                if let Some(_mname) = mi.name.as_deref() {
184                    path.modules.push(mi.clone());
185                }
186                walker = Some(cur);
187            }
188            // If the immediate parent is a Trait/Impl, keep climbing—those don’t contribute
189            // to the *path on disk* (HTML lives under the module tree).
190            Some(Parent::Trait(tid)) | Some(Parent::Impl(tid)) | Some(Parent::Struct(tid)) => {
191                walker = Some(*tid);
192                path.owner = Some(krate.index.get(tid).unwrap().clone());
193            }
194            None => break,
195        }
196    }
197
198    path.modules.reverse();
199    Some(path)
200}