1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5type DocumentRelations = HashMap<String, (Option<String>, Option<String>, Option<String>)>;
7
8#[derive(Debug, Clone)]
10pub struct BuildEnvironment {
11 pub config: crate::config::BuildConfig,
12 pub domains: HashMap<String, Domain>,
13 pub found_docs: Vec<String>,
14 pub all_docs: HashMap<String, f64>, pub dependencies: HashMap<String, HashSet<PathBuf>>,
16 pub included: HashMap<PathBuf, HashSet<String>>,
17 pub temp_data: HashMap<String, serde_json::Value>,
18 pub ref_context: HashMap<String, serde_json::Value>,
19 pub toctree_includes: HashMap<String, Vec<String>>,
20 pub files_to_rebuild: HashMap<String, HashSet<String>>,
21 pub glob_toctrees: HashSet<String>,
22 pub reread_always: HashSet<String>,
23 pub metadata: HashMap<String, HashMap<String, String>>,
24 pub titles: HashMap<String, String>,
25 pub longtitles: HashMap<String, String>,
26 pub tocs: HashMap<String, String>,
27 pub toc_secnumbers: HashMap<String, HashMap<String, Vec<u32>>>,
28 pub toc_fignumbers: HashMap<String, HashMap<String, HashMap<String, Vec<u32>>>>,
29 pub toc_num_entries: HashMap<String, usize>,
30 pub dlfiles: HashMap<String, (Option<String>, String)>,
31 pub images: HashMap<String, String>,
32}
33
34use std::collections::HashSet;
35
36impl BuildEnvironment {
37 pub fn new(config: crate::config::BuildConfig) -> Self {
38 Self {
39 config,
40 domains: HashMap::new(),
41 found_docs: Vec::new(),
42 all_docs: HashMap::new(),
43 dependencies: HashMap::new(),
44 included: HashMap::new(),
45 temp_data: HashMap::new(),
46 ref_context: HashMap::new(),
47 toctree_includes: HashMap::new(),
48 files_to_rebuild: HashMap::new(),
49 glob_toctrees: HashSet::new(),
50 reread_always: HashSet::new(),
51 metadata: HashMap::new(),
52 titles: HashMap::new(),
53 longtitles: HashMap::new(),
54 tocs: HashMap::new(),
55 toc_secnumbers: HashMap::new(),
56 toc_fignumbers: HashMap::new(),
57 toc_num_entries: HashMap::new(),
58 dlfiles: HashMap::new(),
59 images: HashMap::new(),
60 }
61 }
62
63 pub fn add_document(&mut self, docname: String, mtime: f64) {
65 self.found_docs.push(docname.clone());
66 self.all_docs.insert(docname, mtime);
67 }
68
69 pub fn doc2path(&self, docname: &str) -> PathBuf {
71 PathBuf::from(format!("{}.rst", docname))
72 }
73
74 pub fn collect_relations(&self) -> DocumentRelations {
76 HashMap::new()
78 }
79
80 pub fn doc_needs_update(&self, docname: &str, source_path: &PathBuf) -> bool {
82 if !self.all_docs.contains_key(docname) {
84 return true;
85 }
86
87 if let Ok(metadata) = std::fs::metadata(source_path) {
89 if let Ok(mtime) = metadata.modified() {
90 let file_mtime = mtime
91 .duration_since(std::time::UNIX_EPOCH)
92 .unwrap()
93 .as_secs_f64();
94 if let Some(&env_mtime) = self.all_docs.get(docname) {
95 return file_mtime > env_mtime;
96 }
97 }
98 }
99
100 true
101 }
102
103 pub fn update_domain_object(
105 &mut self,
106 domain_name: &str,
107 obj_type: &str,
108 object: DomainObject,
109 ) {
110 let domain = self
111 .domains
112 .entry(domain_name.to_string())
113 .or_insert_with(|| Domain::new(domain_name));
114 domain.add_object(obj_type, object);
115 }
116
117 pub fn get_all_objects(&self) -> Vec<&DomainObject> {
119 let mut objects = Vec::new();
120 for domain in self.domains.values() {
121 objects.extend(domain.get_all_objects());
122 }
123 objects
124 }
125}
126
127#[derive(Debug, Clone)]
129pub struct Domain {
130 pub name: String,
131 pub label: String,
132 pub object_types: HashMap<String, ObjectType>,
133 pub objects: HashMap<String, Vec<DomainObject>>,
134 pub indices: Vec<DomainIndex>,
135}
136
137impl Domain {
138 pub fn new(name: &str) -> Self {
139 Self {
140 name: name.to_string(),
141 label: name.to_string(),
142 object_types: HashMap::new(),
143 objects: HashMap::new(),
144 indices: Vec::new(),
145 }
146 }
147
148 pub fn add_object(&mut self, obj_type: &str, object: DomainObject) {
149 self.objects
150 .entry(obj_type.to_string())
151 .or_default()
152 .push(object);
153 }
154
155 pub fn get_objects(&self) -> Vec<&DomainObject> {
156 let mut all_objects = Vec::new();
157 for objects in self.objects.values() {
158 all_objects.extend(objects);
159 }
160 all_objects
161 }
162
163 pub fn get_all_objects(&self) -> Vec<&DomainObject> {
164 self.get_objects()
165 }
166
167 pub fn get_objects_by_type(&self, obj_type: &str) -> Option<&Vec<DomainObject>> {
168 self.objects.get(obj_type)
169 }
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ObjectType {
175 pub lname: String,
176 pub roles: Vec<String>,
177 pub attrs: HashMap<String, bool>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct DomainObject {
183 pub name: String,
184 pub display_name: Option<String>,
185 pub object_type: String,
186 pub docname: String,
187 pub anchor: Option<String>,
188 pub priority: i32,
189 pub description: Option<String>,
190 pub signature: Option<String>,
191 pub deprecated: bool,
192}
193
194impl DomainObject {
195 pub fn new(
196 name: String,
197 object_type: String,
198 docname: String,
199 anchor: Option<String>,
200 priority: i32,
201 ) -> Self {
202 Self {
203 name,
204 display_name: None,
205 object_type,
206 docname,
207 anchor,
208 priority,
209 description: None,
210 signature: None,
211 deprecated: false,
212 }
213 }
214
215 pub fn with_display_name(mut self, display_name: String) -> Self {
216 self.display_name = Some(display_name);
217 self
218 }
219
220 pub fn with_description(mut self, description: String) -> Self {
221 self.description = Some(description);
222 self
223 }
224
225 pub fn with_signature(mut self, signature: String) -> Self {
226 self.signature = Some(signature);
227 self
228 }
229
230 pub fn with_deprecated(mut self, deprecated: bool) -> Self {
231 self.deprecated = deprecated;
232 self
233 }
234}
235
236#[derive(Debug, Clone)]
238pub struct DomainIndex {
239 pub name: String,
240 pub localname: String,
241 pub shortname: Option<String>,
242 pub entries: Vec<IndexEntry>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct IndexEntry {
247 pub name: String,
248 pub subentries: Vec<IndexEntry>,
249 pub uri: String,
250 pub display_name: String,
251 pub extra: Option<String>,
252}
253
254pub fn create_standard_domains() -> HashMap<String, Domain> {
256 let mut domains = HashMap::new();
257
258 let mut py_domain = Domain::new("py");
260 py_domain.object_types.insert(
261 "module".to_string(),
262 ObjectType {
263 lname: "module".to_string(),
264 roles: vec!["mod".to_string(), "obj".to_string()],
265 attrs: HashMap::new(),
266 },
267 );
268 py_domain.object_types.insert(
269 "function".to_string(),
270 ObjectType {
271 lname: "function".to_string(),
272 roles: vec!["func".to_string(), "obj".to_string()],
273 attrs: HashMap::new(),
274 },
275 );
276 py_domain.object_types.insert(
277 "class".to_string(),
278 ObjectType {
279 lname: "class".to_string(),
280 roles: vec!["class".to_string(), "obj".to_string()],
281 attrs: HashMap::new(),
282 },
283 );
284 py_domain.object_types.insert(
285 "method".to_string(),
286 ObjectType {
287 lname: "method".to_string(),
288 roles: vec!["meth".to_string(), "obj".to_string()],
289 attrs: HashMap::new(),
290 },
291 );
292 py_domain.object_types.insert(
293 "attribute".to_string(),
294 ObjectType {
295 lname: "attribute".to_string(),
296 roles: vec!["attr".to_string(), "obj".to_string()],
297 attrs: HashMap::new(),
298 },
299 );
300 py_domain.object_types.insert(
301 "exception".to_string(),
302 ObjectType {
303 lname: "exception".to_string(),
304 roles: vec!["exc".to_string(), "obj".to_string()],
305 attrs: HashMap::new(),
306 },
307 );
308 py_domain.object_types.insert(
309 "data".to_string(),
310 ObjectType {
311 lname: "data".to_string(),
312 roles: vec!["data".to_string(), "obj".to_string()],
313 attrs: HashMap::new(),
314 },
315 );
316 domains.insert("py".to_string(), py_domain);
317
318 let mut cpp_domain = Domain::new("cpp");
320 cpp_domain.object_types.insert(
321 "class".to_string(),
322 ObjectType {
323 lname: "class".to_string(),
324 roles: vec!["class".to_string()],
325 attrs: HashMap::new(),
326 },
327 );
328 cpp_domain.object_types.insert(
329 "function".to_string(),
330 ObjectType {
331 lname: "function".to_string(),
332 roles: vec!["func".to_string()],
333 attrs: HashMap::new(),
334 },
335 );
336 cpp_domain.object_types.insert(
337 "type".to_string(),
338 ObjectType {
339 lname: "type".to_string(),
340 roles: vec!["type".to_string()],
341 attrs: HashMap::new(),
342 },
343 );
344 domains.insert("cpp".to_string(), cpp_domain);
345
346 let mut js_domain = Domain::new("js");
348 js_domain.object_types.insert(
349 "module".to_string(),
350 ObjectType {
351 lname: "module".to_string(),
352 roles: vec!["mod".to_string()],
353 attrs: HashMap::new(),
354 },
355 );
356 js_domain.object_types.insert(
357 "class".to_string(),
358 ObjectType {
359 lname: "class".to_string(),
360 roles: vec!["class".to_string()],
361 attrs: HashMap::new(),
362 },
363 );
364 js_domain.object_types.insert(
365 "function".to_string(),
366 ObjectType {
367 lname: "function".to_string(),
368 roles: vec!["func".to_string()],
369 attrs: HashMap::new(),
370 },
371 );
372 js_domain.object_types.insert(
373 "method".to_string(),
374 ObjectType {
375 lname: "method".to_string(),
376 roles: vec!["meth".to_string()],
377 attrs: HashMap::new(),
378 },
379 );
380 js_domain.object_types.insert(
381 "data".to_string(),
382 ObjectType {
383 lname: "data".to_string(),
384 roles: vec!["data".to_string()],
385 attrs: HashMap::new(),
386 },
387 );
388 domains.insert("js".to_string(), js_domain);
389
390 let mut std_domain = Domain::new("std");
392 std_domain.object_types.insert(
393 "doc".to_string(),
394 ObjectType {
395 lname: "document".to_string(),
396 roles: vec!["doc".to_string()],
397 attrs: HashMap::new(),
398 },
399 );
400 std_domain.object_types.insert(
401 "label".to_string(),
402 ObjectType {
403 lname: "label".to_string(),
404 roles: vec!["ref".to_string()],
405 attrs: HashMap::new(),
406 },
407 );
408 std_domain.object_types.insert(
409 "term".to_string(),
410 ObjectType {
411 lname: "term".to_string(),
412 roles: vec!["term".to_string()],
413 attrs: HashMap::new(),
414 },
415 );
416 std_domain.object_types.insert(
417 "cmdoption".to_string(),
418 ObjectType {
419 lname: "command line option".to_string(),
420 roles: vec!["option".to_string()],
421 attrs: HashMap::new(),
422 },
423 );
424 std_domain.object_types.insert(
425 "envvar".to_string(),
426 ObjectType {
427 lname: "environment variable".to_string(),
428 roles: vec!["envvar".to_string()],
429 attrs: HashMap::new(),
430 },
431 );
432 domains.insert("std".to_string(), std_domain);
433
434 domains
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_build_environment_creation() {
443 let config = crate::config::BuildConfig::default();
444 let env = BuildEnvironment::new(config);
445
446 assert_eq!(env.found_docs.len(), 0);
447 assert_eq!(env.all_docs.len(), 0);
448 assert_eq!(env.domains.len(), 0);
449 }
450
451 #[test]
452 fn test_domain_object_creation() {
453 let obj = DomainObject::new(
454 "test_function".to_string(),
455 "function".to_string(),
456 "test_module".to_string(),
457 Some("test_function".to_string()),
458 1,
459 );
460
461 assert_eq!(obj.name, "test_function");
462 assert_eq!(obj.object_type, "function");
463 assert_eq!(obj.docname, "test_module");
464 assert_eq!(obj.anchor, Some("test_function".to_string()));
465 assert_eq!(obj.priority, 1);
466 }
467
468 #[test]
469 fn test_domain_creation() {
470 let mut domain = Domain::new("py");
471
472 let obj = DomainObject::new(
473 "test_func".to_string(),
474 "function".to_string(),
475 "test".to_string(),
476 None,
477 1,
478 );
479
480 domain.add_object("function", obj);
481
482 assert_eq!(domain.objects.len(), 1);
483 assert_eq!(domain.get_objects().len(), 1);
484 }
485
486 #[test]
487 fn test_standard_domains() {
488 let domains = create_standard_domains();
489
490 assert!(domains.contains_key("py"));
491 assert!(domains.contains_key("cpp"));
492 assert!(domains.contains_key("js"));
493 assert!(domains.contains_key("std"));
494
495 let py_domain = &domains["py"];
496 assert!(py_domain.object_types.contains_key("module"));
497 assert!(py_domain.object_types.contains_key("function"));
498 assert!(py_domain.object_types.contains_key("class"));
499 }
500}