starship/modules/
typst.rs

1use super::{Context, Module, ModuleConfig};
2
3use crate::configs::typst::TypstConfig;
4use crate::formatter::{StringFormatter, VersionFormatter};
5
6/// Creates a module with the current Typst version
7pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
8    let mut module = context.new_module("typst");
9    let config = TypstConfig::try_load(module.config);
10
11    let is_typst_project = context
12        .try_begin_scan()?
13        .set_files(&config.detect_files)
14        .set_extensions(&config.detect_extensions)
15        .set_folders(&config.detect_folders)
16        .is_match();
17
18    if !is_typst_project {
19        return None;
20    }
21
22    let parsed = StringFormatter::new(config.format).and_then(|formatter| {
23        formatter
24            .map_meta(|var, _| match var {
25                "symbol" => Some(config.symbol),
26                _ => None,
27            })
28            .map_style(|variable| match variable {
29                "style" => Some(Ok(config.style)),
30                _ => None,
31            })
32            .map(|variable| match variable {
33                "version" => {
34                    let version = get_typst_config(context)?;
35                    VersionFormatter::format_module_version(
36                        module.get_name(),
37                        &version,
38                        config.version_format,
39                    )
40                    .map(Ok)
41                }
42                _ => None,
43            })
44            .parse(None, Some(context))
45    });
46
47    module.set_segments(match parsed {
48        Ok(segments) => segments,
49        Err(error) => {
50            log::warn!("Error in module `typst`:\n{error}");
51            return None;
52        }
53    });
54
55    Some(module)
56}
57
58fn get_typst_config(context: &Context) -> Option<String> {
59    context
60        .exec_cmd("typst", &["--version"])?
61        .stdout
62        .trim()
63        .strip_prefix("typst ")
64        .and_then(|version| version.split_whitespace().next().map(ToOwned::to_owned))
65}
66
67#[cfg(test)]
68mod tests {
69    use crate::test::ModuleRenderer;
70    use nu_ansi_term::Color;
71    use std::fs::File;
72    use std::io;
73    #[test]
74    fn read_typst_not_present() -> io::Result<()> {
75        let dir = tempfile::tempdir()?;
76
77        let actual = ModuleRenderer::new("typst").path(dir.path()).collect();
78
79        let expected = None;
80        assert_eq!(expected, actual);
81        dir.close()
82    }
83
84    #[test]
85    fn read_typst_present() -> io::Result<()> {
86        let dir = tempfile::tempdir()?;
87
88        File::create(dir.path().join("test.typ"))?.sync_all()?;
89
90        let actual = ModuleRenderer::new("typst").path(dir.path()).collect();
91        let expected = Some(format!(
92            "via {}",
93            Color::Rgb(0, 147, 167).bold().paint("t v0.10 ")
94        ));
95        assert_eq!(expected, actual);
96        dir.close()
97    }
98}