asimov_cli/commands/module/
inspect.rs1use crate::{BoxError, StandardOptions, SysexitsError::*};
4use asimov_module::ModuleName;
5use color_print::{ceprintln, cprintln};
6
7pub async fn inspect(
8 module_name: ModuleName,
9 output: String,
10 _flags: &StandardOptions,
11) -> Result<(), BoxError> {
12 let registry = asimov_registry::Registry::default();
13
14 let installed = registry.read_manifest(&module_name).await.map_err(|e| {
15 tracing::error!("failed to read manifest for module `{module_name}`: {e}");
16 if let asimov_registry::error::ManifestError::NotInstalled = e {
17 ceprintln!(
18 "<s,dim>hint:</> Check if the module is installed with: <s>asimov module list</>"
19 );
20 }
21 EX_UNAVAILABLE
22 })?;
23
24 let is_enabled = registry
25 .is_module_enabled(&module_name)
26 .await
27 .map_err(|e| {
28 tracing::error!("failed to check if module is enabled: {e}");
29 EX_UNAVAILABLE
30 })?;
31
32 let manifest = &installed.manifest;
33
34 let conf_vars = manifest
35 .config
36 .as_ref()
37 .map(|c| c.variables.as_slice())
38 .unwrap_or_default();
39
40 let conf_status: Vec<bool> = conf_vars
41 .iter()
42 .map(|var| match manifest.variable(&var.name, Some("default")) {
43 Ok(_) => true,
44 Err(asimov_module::ReadVarError::UnconfiguredVar(_)) => false,
45 Err(e) => {
46 tracing::warn!("failed to read configuration variable `{}`: {e}", var.name);
47 false
48 },
49 })
50 .collect();
51
52 match output.as_str() {
53 "json" => {
54 let config: Vec<serde_json::Value> = conf_vars
55 .iter()
56 .zip(&conf_status)
57 .map(|(var, is_set)| {
58 serde_json::json!({
59 "name": var.name,
60 "description": var.description,
61 "default": var.default_value.as_deref().filter(|_| !var.secret),
62 "secret": var.secret,
63 "required": var.is_required(),
64 "set": is_set,
65 })
66 })
67 .collect();
68
69 println!(
70 "{}",
71 serde_json::to_string_pretty(&serde_json::json!({
72 "manifest": serde_json::to_value(&installed)?,
73 "enabled": is_enabled,
74 "config": config,
75 }))?
76 );
77 },
78 _ => {
79 if is_enabled {
80 cprintln!("<s,g>✓</> <s>{}</> (enabled)", manifest.name);
81 } else {
82 cprintln!("<s,r>✗</> <s>{}</> (disabled)", manifest.name);
83 }
84
85 if let Some(label) = &manifest.label {
86 cprintln!("<s>Label:</> {label}");
87 }
88 if let Some(title) = &manifest.title {
89 cprintln!("<s>Title:</> {title}");
90 }
91 if let Some(summary) = &manifest.summary {
92 cprintln!("<s>Summary:</> {summary}");
93 }
94 if let Some(version) = &installed.version {
95 cprintln!("<s>Version:</> {version}");
96 }
97
98 if !manifest.links.is_empty() {
99 let mut links = manifest.links.clone();
100 crate::sort_links(&manifest.name, &mut links);
101 cprintln!("<s>Links:</>");
102 for link in links {
103 println!(" {link}");
104 }
105 }
106
107 if !manifest.provides.is_empty() {
108 cprintln!("<s>Programs:</>");
109 for program in &manifest.provides.programs {
110 println!(" {program}");
111 }
112 }
113
114 if !manifest.handles.is_empty() {
115 cprintln!("<s>Handles:</>");
116 for (kind, values) in [
117 ("URL protocols", &manifest.handles.url_protocols),
118 ("URL prefixes", &manifest.handles.url_prefixes),
119 ("URL patterns", &manifest.handles.url_patterns),
120 ("file extensions", &manifest.handles.file_extensions),
121 ("content types", &manifest.handles.content_types),
122 ] {
123 if !values.is_empty() {
124 println!(" {kind}: {}", values.join(", "));
125 }
126 }
127 }
128
129 cprintln!("<s>Configuration:</>");
130 if conf_vars.is_empty() {
131 println!(" no configuration variables declared");
132 } else {
133 for (var, is_set) in conf_vars.iter().zip(&conf_status) {
134 if *is_set {
135 cprintln!(" <s,g>✓</> <s>{}</> (set)", var.name);
136 } else if var.is_required() {
137 cprintln!(" <s,r>✗</> <s>{}</> (required)", var.name);
138 } else {
139 cprintln!(" <dim>-</> <s>{}</> (unset)", var.name);
140 }
141 if let Some(description) = &var.description {
142 println!(" {description}");
143 }
144 if let Some(default_value) = &var.default_value {
145 if var.secret {
146 println!(" default: ******");
147 } else {
148 println!(" default: {default_value}");
149 }
150 }
151 }
152 }
153 },
154 }
155
156 let missing: Vec<&str> = conf_vars
159 .iter()
160 .zip(&conf_status)
161 .filter(|(var, is_set)| var.is_required() && !**is_set)
162 .map(|(var, _)| var.name.as_str())
163 .collect();
164
165 if !missing.is_empty() {
166 ceprintln!(
167 "<s,dim>hint:</> Configure the missing variable(s) interactively with: <s>asimov module config setup {module_name}</>"
168 );
169 return Err(EX_CONFIG.into());
170 }
171
172 Ok(())
173}