1use crate::env::numbers::clean_astext;
12use crate::env::BuildEnvironment;
13use crate::inventory::InvObject;
14
15pub fn std_search_priority(objtype: &str) -> i32 {
21 match objtype {
22 "term" | "token" | "label" | "doc" => -1,
23 _ => 1,
24 }
25}
26
27pub fn object_type_name(domain: &str, objtype: &str) -> String {
34 match domain {
35 "std" => match objtype {
36 "cmdoption" => "program option",
37 "confval" => "configuration value",
38 "envvar" => "environment variable",
39 "term" => "glossary term",
40 "token" => "grammar token",
41 "label" => "reference label",
42 "doc" => "document",
43 other => other,
44 }
45 .to_string(),
46 "py" => {
47 let lname = match objtype {
48 "function" | "data" | "class" | "exception" | "method" | "attribute"
49 | "property" | "module" => objtype,
50 "classmethod" => "class method",
51 "staticmethod" => "static method",
52 "type" => "type alias",
53 _ => return objtype.to_string(),
54 };
55 format!("Python {lname}")
56 }
57 _ => objtype.to_string(),
58 }
59}
60
61pub fn domain_objects(env: &BuildEnvironment) -> Vec<(&'static str, Vec<InvObject>)> {
64 vec![
65 ("py", py_domain_objects(env)),
66 ("std", std_domain_objects(env)),
67 ]
68}
69
70pub fn std_domain_objects(env: &BuildEnvironment) -> Vec<InvObject> {
72 let mut objects = Vec::new();
73
74 for docname in env.all_docs.keys() {
76 objects.push(InvObject {
77 name: docname.clone(),
78 objtype: "doc".to_string(),
79 priority: -1,
80 docname: docname.clone(),
81 anchor: String::new(),
82 dispname: env
83 .titles
84 .get(docname)
85 .map(clean_astext)
86 .unwrap_or_default(),
87 });
88 }
89
90 for ((program, option), (docname, anchor)) in &env.std.progoptions {
91 let fullname = match program {
92 Some(program) => format!("{program}.{option}"),
93 None => option.clone(),
94 };
95 objects.push(InvObject {
96 name: fullname.clone(),
97 objtype: "cmdoption".to_string(),
98 priority: 1,
99 docname: docname.clone(),
100 anchor: anchor.clone(),
101 dispname: fullname,
102 });
103 }
104
105 for ((objtype, name), (docname, anchor)) in &env.std.objects {
106 objects.push(InvObject {
107 name: name.clone(),
108 objtype: objtype.clone(),
109 priority: std_search_priority(objtype),
110 docname: docname.clone(),
111 anchor: anchor.clone(),
112 dispname: name.clone(),
113 });
114 }
115
116 for (name, (docname, labelid, sectionname)) in &env.std.labels {
117 objects.push(InvObject {
118 name: name.clone(),
119 objtype: "label".to_string(),
120 priority: -1,
121 docname: docname.clone(),
122 anchor: labelid.clone(),
123 dispname: sectionname.clone(),
124 });
125 }
126
127 for (name, (docname, labelid)) in &env.std.anonlabels {
129 if !env.std.labels.contains_key(name) {
130 objects.push(InvObject {
131 name: name.clone(),
132 objtype: "label".to_string(),
133 priority: -1,
134 docname: docname.clone(),
135 anchor: labelid.clone(),
136 dispname: name.clone(),
137 });
138 }
139 }
140
141 sort_like_sphinx(&mut objects);
142 objects
143}
144
145pub fn py_domain_objects(env: &BuildEnvironment) -> Vec<InvObject> {
149 let mut objects = Vec::new();
150 for (name, module) in &env.py.modules {
151 objects.push(InvObject {
152 name: name.clone(),
153 objtype: "module".to_string(),
154 priority: 0,
155 docname: module.docname.clone(),
156 anchor: module.node_id.clone(),
157 dispname: name.clone(),
158 });
159 }
160 for (name, object) in &env.py.objects {
161 if object.objtype == "module" {
162 continue;
163 }
164 objects.push(InvObject {
165 name: name.clone(),
166 objtype: object.objtype.clone(),
167 priority: if object.aliased { -1 } else { 1 },
168 docname: object.docname.clone(),
169 anchor: object.node_id.clone(),
170 dispname: name.clone(),
171 });
172 }
173 sort_like_sphinx(&mut objects);
174 objects
175}
176
177fn sort_like_sphinx(objects: &mut [InvObject]) {
180 objects.sort_by(|a, b| {
181 a.name
182 .cmp(&b.name)
183 .then_with(|| a.dispname.cmp(&b.dispname))
184 .then_with(|| a.objtype.cmp(&b.objtype))
185 .then_with(|| a.docname.cmp(&b.docname))
186 .then_with(|| a.anchor.cmp(&b.anchor))
187 .then_with(|| a.priority.cmp(&b.priority))
188 });
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn std_types_the_table_does_not_name_are_searchable() {
197 assert_eq!(std_search_priority("term"), -1);
198 assert_eq!(std_search_priority("doc"), -1);
199 assert_eq!(std_search_priority("confval"), 1);
200 assert_eq!(std_search_priority("envvar"), 1);
201 assert_eq!(std_search_priority("cmdoption"), 1);
202 assert_eq!(std_search_priority("custom-type"), 1);
203 }
204
205 #[test]
206 fn type_names_follow_get_type_name() {
207 assert_eq!(object_type_name("std", "envvar"), "environment variable");
208 assert_eq!(object_type_name("std", "custom"), "custom");
209 assert_eq!(object_type_name("py", "class"), "Python class");
210 assert_eq!(object_type_name("py", "classmethod"), "Python class method");
211 assert_eq!(object_type_name("py", "type"), "Python type alias");
212 assert_eq!(object_type_name("py", "unknown"), "unknown");
213 }
214
215 #[test]
216 fn modules_are_listed_once_from_the_module_registry() {
217 use crate::env::py_domain::{PyModuleEntry, PyObjectEntry};
218
219 let mut env = BuildEnvironment::default();
220 env.py.note_module(
221 "pkg",
222 PyModuleEntry {
223 docname: "api".to_string(),
224 node_id: "module-pkg".to_string(),
225 synopsis: String::new(),
226 platform: String::new(),
227 deprecated: false,
228 },
229 );
230 env.py.note_object(
231 "pkg",
232 PyObjectEntry {
233 docname: "api".to_string(),
234 node_id: "module-pkg".to_string(),
235 objtype: "module".to_string(),
236 aliased: false,
237 },
238 );
239 env.py.note_object(
240 "pkg.run",
241 PyObjectEntry {
242 docname: "api".to_string(),
243 node_id: "pkg.run".to_string(),
244 objtype: "function".to_string(),
245 aliased: false,
246 },
247 );
248 let rows: Vec<(String, String, i32)> = py_domain_objects(&env)
249 .into_iter()
250 .map(|o| (o.name, o.objtype, o.priority))
251 .collect();
252 assert_eq!(
253 rows,
254 vec![
255 ("pkg".to_string(), "module".to_string(), 0),
256 ("pkg.run".to_string(), "function".to_string(), 1),
257 ]
258 );
259 }
260}