rust_utils/linux/
manpage.rs

1use std::{
2    fs,
3    path::Path,
4    collections::HashMap
5};
6use crate::chainable;
7use chrono::{Local, Datelike, Timelike};
8use super::MONTHS;
9
10/// Basic unix manpage generator
11#[chainable]
12pub struct ManpageBuilder {
13    name: String,
14    short_desc: String,
15    desc: String,
16    author_name: String,
17    author_email: String,
18
19    #[chainable(collapse_option, use_into_impl, doc = "Add in a comment next to the name in the file comment")]
20    name_comment: Option<String>,
21    other_pages: Vec<String>,
22    other_sections: HashMap<String, String>
23}
24
25impl ManpageBuilder {
26    /// Create a new `ManpageBuilder`
27    pub fn new(
28        name: &str,
29        short_desc: &str,
30        author_name: &str,
31        author_email: &str,
32        desc: &str
33    ) -> Self {
34        ManpageBuilder {
35            name: name.to_string(),
36            short_desc: short_desc.to_string(),
37            author_name: author_name.to_string(),
38            author_email: author_email.to_string(),
39            desc: desc.replace("\n", "\n.br\n"),
40            name_comment: None,
41            other_pages: vec![],
42            other_sections: HashMap::new()
43        }
44    }
45
46    /// Add a link to a page in the "SEE ALSO" section
47    pub fn other_page(mut self, name: &str) -> Self {
48        self.other_pages.push(name.to_string());
49        self
50    }
51
52    /// Add in a section
53    ///
54    /// The name should be in all capital letters
55    pub fn section(mut self, name: &str, text: &str) -> Self {
56        self.other_sections.insert(name.to_string(), text.replace("\n", "\n.br\n"));
57        self
58    }
59
60    /// Generate and save the manpage at the specified path
61    pub fn build<P: AsRef<Path>>(&self, app_ver: &str, path: P) {
62        // get the current date and time (formatted)
63        let date = Local::now().date_naive();
64        let month = MONTHS[date.month() as usize - 1];
65        let day = date.day();
66        let year = date.year();
67        let date_str = format!("{day} {month} {year}");
68        let time = Local::now().time();
69        let hour = time.hour();
70        let minute = time.minute();
71        let second = time.second();
72        let time_str = format!("{hour:02}:{minute:02}:{second:02}");
73
74        let syn_str = if let Some(synopsis) = self.other_sections.get("SYNOPSIS") {
75            format!(
76                ".SH SYNOPSIS\n\
77                {synopsis}\n"
78            )
79        }
80        else { String::new() };
81
82        let examples_str = if let Some(examples) = self.other_sections.get("EXAMPLES") {
83            format!(
84                ".SH EXAMPLES\n\
85                {examples}\n"
86            )
87        }
88        else { String::new() };
89
90        let name_comment = if let Some(ref comment) = self.name_comment {
91                format!(" ({comment})")
92            }
93            else { String::new() };
94
95        let header_str = format!(
96            ".\\\" Manpage for {name}{name_comment}.\n\
97            .\\\" Created on {month} {day}, {year} at {time_str}\n\
98            .TH {name} 1 \"{date_str}\" \"{app_ver}\" \"{name} man page\"\n\
99            .SH NAME\n\
100            {name} \\- {}\n\
101            {syn_str}\
102            .SH DESCRIPTION\n\
103            {}\n\
104            {examples_str}",
105            self.short_desc,
106            self.desc,
107            name = self.name,
108        );
109
110        let author_str = format!(
111            ".SH AUTHOR\n\
112            {} ({})",
113            self.author_name,
114            self.author_email
115        );
116
117        let footer_str = if self.other_pages.len() > 0 {
118            let mut sect_header = String::from(".SH SEE ALSO\n");
119            for (i, other_page) in self.other_pages.iter().enumerate() {
120                sect_header.push_str(&format!("{other_page}(1)\n"));
121
122                if i < self.other_pages.len() - 1{
123                    sect_header.push_str(".br\n");
124                }
125            }
126
127            format!("{sect_header}{author_str}")
128        }
129        else { author_str };
130
131        let mut body_str = String::new();
132
133        for (sect_title, sect_body) in &self.other_sections {
134            if sect_title != "SYNOPSIS" && sect_title != "EXAMPLES" {
135                body_str.push_str(&format!(
136                    ".SH {sect_title}\n\
137                    {sect_body}\n"
138                ));
139            }
140        }
141
142        let file_str = format!("{header_str}{body_str}{footer_str}");
143        fs::remove_file(&path).unwrap_or(());
144        fs::write(&path, &file_str).unwrap();
145    }
146}