asimov_cli/
shared.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::Result;
4use asimov_module::{ModuleManifest, resolve::Module};
5use clientele::{Subcommand, SubcommandsProvider, SysexitsError::*};
6use color_print::{ceprintln, cstr};
7use std::rc::Rc;
8
9/// Locates the given subcommand or prints an error.
10pub fn locate_subcommand(name: &str) -> Result<Subcommand> {
11    match SubcommandsProvider::find("asimov-", name) {
12        Some(cmd) => Ok(cmd),
13        None => {
14            eprintln!("asimov: command not found: asimov-{}", name);
15            Err(EX_UNAVAILABLE)
16        },
17    }
18}
19
20const NO_MODULES_FOUND_HINT: &str = cstr!(
21    r#"<s,dim>hint:</> There appears to be no installed modules.
22<s,dim>hint:</> Modules may be discovered either on the site <u>https://asimov.directory/modules</>
23<s,dim>hint:</> or on the GitHub organization <u>https://github.com/asimov-modules</>
24<s,dim>hint:</> and installed with <s>asimov module install <<module>></>"#
25);
26
27pub async fn installed_modules(
28    registry: &asimov_registry::Registry,
29    filter: Option<&str>,
30) -> Result<Vec<ModuleManifest>> {
31    let modules = registry
32        .installed_modules()
33        .await
34        .map_err(|e| {
35            ceprintln!("<s,r>error:</> unable to access installed modules: {e}");
36            match e {
37                asimov_registry::error::InstalledModulesError::DirIo(_, err)
38                    if err.kind() == std::io::ErrorKind::NotFound =>
39                {
40                    ceprintln!("{NO_MODULES_FOUND_HINT}");
41                },
42                _ => (),
43            }
44            EX_UNAVAILABLE
45        })?
46        .into_iter()
47        .map(|manifest| manifest.manifest)
48        .filter(|manifest| {
49            if let Some(filter) = filter {
50                manifest
51                    .provides
52                    .programs
53                    .iter()
54                    .any(|program| program.split('-').next_back().is_some_and(|p| p == filter))
55            } else {
56                true
57            }
58        })
59        .collect();
60
61    Ok(modules)
62}
63
64pub async fn pick_module(
65    registry: &asimov_registry::Registry,
66    url: impl AsRef<str>,
67    modules: &[Rc<Module>],
68    filter: Option<&str>,
69) -> Result<Rc<Module>> {
70    let url = url.as_ref();
71
72    if let Some(filter) = filter {
73        let module = modules.iter().find(|m| m.name == filter).ok_or_else(|| {
74                ceprintln!("<s,r>error:</> failed to find a module named `{filter}` that supports handling the URL <s>{url}</>");
75                EX_SOFTWARE
76            })?;
77
78        if !registry
79            .is_module_enabled(&module.name)
80            .await
81            .map_err(|e| {
82                ceprintln!(
83                    "<s,r>error:</> error while checking whether module <s>{}</> is enabled: {e}",
84                    module.name
85                );
86                EX_IOERR
87            })?
88        {
89            ceprintln!(
90                "<s,r>error:</> module <s>{}</> is not enabled.",
91                module.name
92            );
93            ceprintln!(
94                "<s,dim>hint:</> It can be enabled with: <s>asimov module enable {}</>",
95                module.name
96            );
97            Err(EX_UNAVAILABLE)
98        } else {
99            Ok(module.clone())
100        }
101    } else {
102        let mut iter = modules.iter();
103        loop {
104            let module = iter.next().ok_or_else(|| {
105                    ceprintln!(
106                        "<s,r>error:</> failed to find a module to handle the URL <s>{url}</>"
107                    );
108                    let module_count = modules.len();
109                    if module_count > 0 {
110                        if module_count == 1 {
111                            ceprintln!("<s,dim>hint:</> Found <s>{module_count}</> installed module that could handle this URL but is disabled.");
112                        } else {
113                            ceprintln!("<s,dim>hint:</> Found <s>{module_count}</> installed modules that could handle this URL but are disabled.");
114                        }
115                        ceprintln!("<s,dim>hint:</> A module can be enabled with: <s>asimov module enable <<module>></>");
116                        ceprintln!("<s,dim>hint:</> Available modules:");
117                        for module in modules {
118                            ceprintln!("<s,dim>hint:</>\t<s>{}</>", module.name);
119                        }
120                    }
121                    EX_UNAVAILABLE
122                })?;
123
124            if registry
125                .is_module_enabled(&module.name)
126                .await
127                .map_err(|e| {
128                    ceprintln!(
129                        "<s,r>error:</> error while checking whether module <s>{}</> is enabled: {e}",
130                        module.name
131                    );
132                    EX_IOERR
133                })?
134            {
135                return Ok(module.clone());
136            }
137        }
138    }
139}