Skip to main content

mant_core/
catalog.rs

1//! Unifies registered Markdown and indexed manual pages for discovery clients.
2
3use std::path::PathBuf;
4
5use crate::{RegisteredDocumentOrigin, list_registered_documents, system_manual_index};
6
7/// Source family used to resolve one available document.
8#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
9pub enum AvailableDocumentKind {
10    Markdown,
11    Manual,
12}
13
14/// Precedence class and storage family for one available document.
15#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
16pub enum AvailableDocumentOrigin {
17    User,
18    System,
19    ManualPath,
20}
21
22/// One document discoverable by name through the ordinary query boundary.
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct AvailableDocument {
25    pub name: String,
26    pub kind: AvailableDocumentKind,
27    pub section: Option<String>,
28    pub path: PathBuf,
29    pub origin: AvailableDocumentOrigin,
30}
31
32/// List every effective registered document and locally indexed manual page.
33#[must_use]
34pub fn list_available_documents() -> Vec<AvailableDocument> {
35    list_available_documents_from(list_registered_documents(), system_manual_index().pages())
36}
37
38fn list_available_documents_from(
39    registered: Vec<crate::RegisteredDocument>,
40    manuals: &[crate::ManualPage],
41) -> Vec<AvailableDocument> {
42    let mut documents = registered
43        .into_iter()
44        .map(|document| AvailableDocument {
45            name: document.name,
46            kind: AvailableDocumentKind::Markdown,
47            section: None,
48            path: document.path,
49            origin: match document.origin {
50                RegisteredDocumentOrigin::User => AvailableDocumentOrigin::User,
51                RegisteredDocumentOrigin::System => AvailableDocumentOrigin::System,
52            },
53        })
54        .chain(manuals.iter().map(|page| AvailableDocument {
55            name: page.name.clone(),
56            kind: AvailableDocumentKind::Manual,
57            section: Some(page.section.clone()),
58            path: page.path.clone(),
59            origin: AvailableDocumentOrigin::ManualPath,
60        }))
61        .collect::<Vec<_>>();
62    documents.sort_unstable_by(|left, right| {
63        (&left.name, left.kind, &left.section).cmp(&(&right.name, right.kind, &right.section))
64    });
65    documents
66}
67
68#[cfg(test)]
69mod tests {
70    use std::path::PathBuf;
71
72    use crate::{ManualPage, RegisteredDocument, RegisteredDocumentOrigin};
73
74    use super::{AvailableDocumentKind, AvailableDocumentOrigin, list_available_documents_from};
75
76    #[test]
77    fn merges_both_namespaces_without_hiding_manual_sections() {
78        let documents = list_available_documents_from(
79            vec![RegisteredDocument {
80                name: "printf".to_owned(),
81                path: PathBuf::from("/home/demo/.local/share/mant/documents/printf.md"),
82                origin: RegisteredDocumentOrigin::User,
83            }],
84            &[
85                ManualPage {
86                    name: "printf".to_owned(),
87                    section: "1".to_owned(),
88                    path: PathBuf::from("/usr/share/man/man1/printf.1.gz"),
89                },
90                ManualPage {
91                    name: "printf".to_owned(),
92                    section: "3".to_owned(),
93                    path: PathBuf::from("/usr/share/man/man3/printf.3.gz"),
94                },
95            ],
96        );
97
98        assert_eq!(documents.len(), 3);
99        assert_eq!(documents[0].kind, AvailableDocumentKind::Markdown);
100        assert_eq!(documents[0].origin, AvailableDocumentOrigin::User);
101        assert_eq!(documents[1].section.as_deref(), Some("1"));
102        assert_eq!(documents[2].section.as_deref(), Some("3"));
103    }
104}