Skip to main content

harn_cli/package/registry/
commands.rs

1//! The `harn package`/`harn registry` CLI entry points — argument handling,
2//! human and JSON rendering, and process exit codes.
3
4use crate::package::*;
5
6pub fn list_package_cache() {
7    let result = (|| -> Result<(PathBuf, Vec<PackageCacheEntry>), PackageError> {
8        Ok((cache_root()?, discover_package_cache_entries()?))
9    })();
10
11    match result {
12        Ok((root, entries)) => {
13            println!("Cache root: {}", root.display());
14            if entries.is_empty() {
15                println!("No cached packages.");
16                return;
17            }
18            println!("kind\tkey\tcontent_hash\tsource\tpath");
19            for entry in entries {
20                let (source, content_hash) = entry
21                    .metadata
22                    .as_ref()
23                    .map(|metadata| (metadata.source.as_str(), metadata.content_hash.as_str()))
24                    .unwrap_or(("(unknown)", "(unknown)"));
25                println!(
26                    "{}\t{}\t{}\t{}\t{}",
27                    entry.kind,
28                    entry.commit,
29                    content_hash,
30                    source,
31                    entry.path.display()
32                );
33            }
34        }
35        Err(error) => {
36            eprintln!("error: {error}");
37            process::exit(1);
38        }
39    }
40}
41
42pub fn clean_package_cache(all: bool) {
43    match clean_package_cache_impl(all) {
44        Ok(removed) => println!("Removed {removed} cached package entries."),
45        Err(error) => {
46            eprintln!("error: {error}");
47            process::exit(1);
48        }
49    }
50}
51
52pub fn verify_package_cache(materialized: bool) {
53    match verify_package_cache_impl(materialized) {
54        Ok(verified) => println!("Verified {verified} package cache entries."),
55        Err(error) => {
56            eprintln!("error: {error}");
57            process::exit(1);
58        }
59    }
60}
61
62pub fn search_package_registry(query: Option<&str>, registry: Option<&str>, json: bool) {
63    match search_package_registry_impl(query, registry) {
64        Ok(packages) if json => {
65            println!(
66                "{}",
67                serde_json::to_string_pretty(&packages)
68                    .unwrap_or_else(|error| format!(r#"{{"error":"{error}"}}"#))
69            );
70        }
71        Ok(packages) => {
72            if packages.is_empty() {
73                println!("No packages found.");
74                return;
75            }
76            println!("name\tlatest\tharn\tcontract\tdescription");
77            for package in packages {
78                let latest = latest_registry_version(&package)
79                    .map(|version| version.version.as_str())
80                    .unwrap_or("-");
81                println!(
82                    "{}\t{}\t{}\t{}\t{}",
83                    package.name,
84                    latest,
85                    package.harn.as_deref().unwrap_or("-"),
86                    package.connector_contract.as_deref().unwrap_or("-"),
87                    package.description.as_deref().unwrap_or("")
88                );
89            }
90        }
91        Err(error) => {
92            eprintln!("error: {error}");
93            process::exit(1);
94        }
95    }
96}
97
98pub fn search_rule_package_registry(query: Option<&str>, registry: Option<&str>, json: bool) {
99    match search_rule_package_registry_impl(query, registry) {
100        Ok(packages) if json => {
101            println!(
102                "{}",
103                serde_json::to_string_pretty(&packages)
104                    .unwrap_or_else(|error| format!(r#"{{"error":"{error}"}}"#))
105            );
106        }
107        Ok(packages) => {
108            if packages.is_empty() {
109                println!("No rule packs found.");
110                return;
111            }
112            println!("name\tlatest\tlanguages\trules\tsafety\tdescription");
113            for package in packages {
114                let latest = latest_registry_version(&package)
115                    .map(|version| version.version.as_str())
116                    .unwrap_or("-")
117                    .to_string();
118                let rule_pack = package.rule_pack.unwrap_or_default();
119                let languages = if rule_pack.languages.is_empty() {
120                    "-".to_string()
121                } else {
122                    rule_pack.languages.join(",")
123                };
124                let safety = if rule_pack.safety_summary.is_empty() {
125                    "-".to_string()
126                } else {
127                    rule_pack.safety_summary.join(",")
128                };
129                println!(
130                    "{}\t{}\t{}\t{}\t{}\t{}",
131                    package.name,
132                    latest,
133                    languages,
134                    rule_pack.rule_count,
135                    safety,
136                    package.description.as_deref().unwrap_or("")
137                );
138            }
139        }
140        Err(error) => {
141            eprintln!("error: {error}");
142            process::exit(1);
143        }
144    }
145}
146
147pub fn show_package_registry_info(spec: &str, registry: Option<&str>, json: bool) {
148    match package_registry_info_impl(spec, registry) {
149        Ok(info) if json => {
150            println!(
151                "{}",
152                serde_json::to_string_pretty(&info)
153                    .unwrap_or_else(|error| format!(r#"{{"error":"{error}"}}"#))
154            );
155        }
156        Ok(info) => {
157            let package = info.package;
158            println!("{}", package.name);
159            if let Some(description) = package.description.as_deref() {
160                println!("description: {description}");
161            }
162            println!("repository: {}", package.repository);
163            if let Some(license) = package.license.as_deref() {
164                println!("license: {license}");
165            }
166            if let Some(harn) = package.harn.as_deref() {
167                println!("harn: {harn}");
168            }
169            if let Some(contract) = package.connector_contract.as_deref() {
170                println!("connector_contract: {contract}");
171            }
172            if let Some(docs) = package.docs_url.as_deref() {
173                println!("docs: {docs}");
174            }
175            if let Some(checksum) = package.checksum.as_deref() {
176                println!("checksum: {checksum}");
177            }
178            if let Some(provenance) = package.provenance.as_deref() {
179                println!("provenance: {provenance}");
180            }
181            if !package.exports.is_empty() {
182                println!("exports: {}", package.exports.join(", "));
183            }
184            if let Some(rule_pack) = package.rule_pack.as_ref() {
185                println!("rule_pack: yes");
186                println!("rules: {}", rule_pack.rule_count);
187                if !rule_pack.languages.is_empty() {
188                    println!("languages: {}", rule_pack.languages.join(", "));
189                }
190                if !rule_pack.safety_summary.is_empty() {
191                    println!("safety: {}", rule_pack.safety_summary.join(", "));
192                }
193            }
194            if let Some(version) = info.selected_version {
195                println!("selected: {}", version.version);
196                if let Some(git) = version.git.as_deref() {
197                    println!("git: {git}");
198                }
199                if let Some(archive) = version.archive.as_deref() {
200                    println!("archive: {archive}");
201                }
202                if let Some(rev) = version.rev.as_deref() {
203                    println!("rev: {rev}");
204                }
205                if let Some(branch) = version.branch.as_deref() {
206                    println!("branch: {branch}");
207                }
208                if let Some(package_name) = version.package.as_deref() {
209                    println!("package: {package_name}");
210                }
211            }
212            if !package.versions.is_empty() {
213                let versions = package
214                    .versions
215                    .iter()
216                    .map(|version| {
217                        if version.yanked {
218                            format!("{} (yanked)", version.version)
219                        } else {
220                            version.version.clone()
221                        }
222                    })
223                    .collect::<Vec<_>>()
224                    .join(", ");
225                println!("versions: {versions}");
226            }
227        }
228        Err(error) => {
229            eprintln!("error: {error}");
230            process::exit(1);
231        }
232    }
233}