1use crate::Result;
4use asimov_module::{ModuleManifest, resolve::Module};
5use clientele::{Subcommand, SubcommandsProvider, SysexitsError::*};
6use color_print::{ceprintln, cstr};
7use std::rc::Rc;
8
9pub 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}