Skip to content

Commit f9a745a

Browse files
authored
refactor(ui): type registry dispatch in templates.rs (#967)
Completes #369. The UI templates were the last string-dispatch sites: render_package_detail, render_registry_list_paginated, and get_registry_icon / get_registry_title matched on registry_type string literals. Parse once to RegistryType and match on the enum (icon/title become exhaustive matches — a new variant is now a compile error there too). No behavioral change: mount points, install commands, icons and titles are identical. The type-safety ratchet drops from 53 to 0 string-dispatch sites.
1 parent 6bd55f4 commit f9a745a

1 file changed

Lines changed: 63 additions & 59 deletions

File tree

nora-registry/src/ui/templates.rs

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use super::api::{DashboardResponse, DockerDetail, MavenDetail, PackageDetail, PackageMetadata};
55
use super::components::*;
66
use super::i18n::{get_translations, Lang};
7+
use crate::registry_type::RegistryType;
78
use crate::repo_index::RepoInfo;
89
use crate::tokens::TokenListEntry;
910
use std::fmt::Write;
@@ -274,9 +275,9 @@ pub fn render_registry_list_paginated(
274275
.join("")
275276
};
276277

277-
let version_label = match registry_type {
278-
"docker" => t.tags,
279-
"raw" | "rpm" | "deb" => t.items,
278+
let version_label = match RegistryType::from_str_opt(registry_type) {
279+
Some(RegistryType::Docker) => t.tags,
280+
Some(RegistryType::Raw | RegistryType::Rpm | RegistryType::Deb) => t.items,
280281
_ => t.versions,
281282
};
282283

@@ -1079,10 +1080,11 @@ pub fn render_package_detail(
10791080
auth_enabled: bool,
10801081
) -> String {
10811082
let _t = get_translations(lang);
1083+
let rt = RegistryType::from_str_opt(registry_type);
10821084
let icon = get_registry_icon(registry_type);
10831085
let registry_title = get_registry_title(registry_type);
10841086

1085-
let file_icon = if registry_type == "raw" {
1087+
let file_icon = if rt == Some(RegistryType::Raw) {
10861088
r#"<svg class="w-4 h-4 flex-shrink-0 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>"#
10871089
} else {
10881090
""
@@ -1152,50 +1154,52 @@ pub fn render_package_detail(
11521154
String::new()
11531155
};
11541156

1155-
let install_cmd = match registry_type {
1156-
"npm" => format!("npm install {} --registry {}/npm", name, base_url),
1157-
"cargo" => format!("cargo add {}", name),
1158-
"pypi" => format!("pip install {} --index-url {}/simple", name, base_url),
1159-
"go" => format!("GOPROXY={}/go go get {}", base_url, name),
1160-
"raw" => {
1157+
let install_cmd = match rt {
1158+
Some(RegistryType::Npm) => format!("npm install {} --registry {}/npm", name, base_url),
1159+
Some(RegistryType::Cargo) => format!("cargo add {}", name),
1160+
Some(RegistryType::PyPI) => {
1161+
format!("pip install {} --index-url {}/simple", name, base_url)
1162+
}
1163+
Some(RegistryType::Go) => format!("GOPROXY={}/go go get {}", base_url, name),
1164+
Some(RegistryType::Raw) => {
11611165
if detail.versions.len() == 1 && detail.versions[0].version == name {
11621166
// Root-level file — direct download URL
11631167
format!("curl -O {}/raw/{}", base_url, name)
11641168
} else {
11651169
format!("curl -O {}/raw/{}/<file>", base_url, name)
11661170
}
11671171
}
1168-
"nuget" => format!(
1172+
Some(RegistryType::Nuget) => format!(
11691173
"dotnet add package {} --source {}/nuget/v3/index.json",
11701174
name, base_url
11711175
),
1172-
"gems" => format!("gem install {} --source {}/gems", name, base_url),
1173-
"terraform" => format!(
1176+
Some(RegistryType::Gems) => format!("gem install {} --source {}/gems", name, base_url),
1177+
Some(RegistryType::Terraform) => format!(
11741178
"# In required_providers block:\n source = \"{}/terraform/{}\"",
11751179
base_url
11761180
.trim_start_matches("https://")
11771181
.trim_start_matches("http://"),
11781182
name
11791183
),
1180-
"ansible" => format!("ansible-galaxy collection install {}", name),
1181-
"pub" => format!(
1184+
Some(RegistryType::Ansible) => format!("ansible-galaxy collection install {}", name),
1185+
Some(RegistryType::PubDart) => format!(
11821186
"# pubspec.yaml:\n hosted: {}/pub\n # then: dart pub get",
11831187
base_url
11841188
),
1185-
"conan" => format!("conan install --requires={}/ -r nora", name),
1186-
"rpm" => format!(
1189+
Some(RegistryType::Conan) => format!("conan install --requires={}/ -r nora", name),
1190+
Some(RegistryType::Rpm) => format!(
11871191
"dnf config-manager --add-repo {}/rpm/{}\n# then set gpgcheck=0 repo_gpgcheck=0 in the .repo file",
11881192
base_url, name
11891193
),
1190-
"deb" => format!(
1194+
Some(RegistryType::Deb) => format!(
11911195
"echo 'deb [trusted=yes] {}/deb/{} ./' | sudo tee /etc/apt/sources.list.d/nora-{}.list && sudo apt-get update",
11921196
base_url, name, name
11931197
),
11941198
_ => String::new(),
11951199
};
11961200

11971201
// Build breadcrumbs — make each path segment clickable for hierarchical names
1198-
let breadcrumb_html = if registry_type == "ansible" && name.contains('.') {
1202+
let breadcrumb_html = if rt == Some(RegistryType::Ansible) && name.contains('.') {
11991203
// Ansible: community.general → Ansible Galaxy / community / general
12001204
let parts: Vec<&str> = name.splitn(2, '.').collect();
12011205
let mut crumbs = format!(
@@ -1255,7 +1259,7 @@ pub fn render_package_detail(
12551259
)
12561260
};
12571261

1258-
let detail_title = if registry_type == "raw" && name.contains('/') {
1262+
let detail_title = if rt == Some(RegistryType::Raw) && name.contains('/') {
12591263
html_escape(name.rsplit('/').next().unwrap_or(name))
12601264
} else {
12611265
html_escape(name)
@@ -1269,7 +1273,7 @@ pub fn render_package_detail(
12691273
};
12701274

12711275
// JavaScript for per-version install command (NuGet-specific)
1272-
let version_js = if registry_type == "nuget" {
1276+
let version_js = if rt == Some(RegistryType::Nuget) {
12731277
format!(
12741278
r##"<script>
12751279
document.querySelectorAll('.version-row').forEach(function(row) {{
@@ -1351,19 +1355,19 @@ if (copyBtn) {{ copyBtn.addEventListener('click', function() {{
13511355
install_label = _t.install_command,
13521356
cmd = html_escape(&install_cmd),
13531357
metadata_panel = metadata_panel,
1354-
versions_label = if registry_type == "raw" {
1358+
versions_label = if rt == Some(RegistryType::Raw) {
13551359
_t.files
1356-
} else if registry_type == "rpm" || registry_type == "deb" {
1360+
} else if matches!(rt, Some(RegistryType::Rpm | RegistryType::Deb)) {
13571361
_t.items
13581362
} else {
13591363
_t.versions
13601364
},
13611365
total = display_total,
13621366
total_word = _t.total,
13631367
prerelease = prerelease_toggle,
1364-
col_version = if registry_type == "raw" {
1368+
col_version = if rt == Some(RegistryType::Raw) {
13651369
_t.filename
1366-
} else if registry_type == "rpm" || registry_type == "deb" {
1370+
} else if matches!(rt, Some(RegistryType::Rpm | RegistryType::Deb)) {
13671371
_t.items
13681372
} else {
13691373
_t.versions
@@ -1732,46 +1736,46 @@ fn render_role_badge(role: &crate::tokens::Role) -> String {
17321736

17331737
/// Returns SVG icon path for the registry type
17341738
fn get_registry_icon(registry_type: &str) -> &'static str {
1735-
match registry_type {
1736-
"docker" => icons::DOCKER,
1737-
"maven" => icons::MAVEN,
1738-
"npm" => icons::NPM,
1739-
"cargo" => icons::CARGO,
1740-
"pypi" => icons::PYPI,
1741-
"go" => icons::GO,
1742-
"raw" => icons::RAW,
1743-
"gems" => icons::GEMS,
1744-
"terraform" => icons::TERRAFORM,
1745-
"ansible" => icons::ANSIBLE,
1746-
"nuget" => icons::NUGET,
1747-
"pub" => icons::PUB,
1748-
"conan" => icons::CONAN,
1749-
"rpm" => icons::RPM,
1750-
"deb" => icons::DEB,
1751-
_ => {
1739+
match RegistryType::from_str_opt(registry_type) {
1740+
Some(RegistryType::Docker) => icons::DOCKER,
1741+
Some(RegistryType::Maven) => icons::MAVEN,
1742+
Some(RegistryType::Npm) => icons::NPM,
1743+
Some(RegistryType::Cargo) => icons::CARGO,
1744+
Some(RegistryType::PyPI) => icons::PYPI,
1745+
Some(RegistryType::Go) => icons::GO,
1746+
Some(RegistryType::Raw) => icons::RAW,
1747+
Some(RegistryType::Gems) => icons::GEMS,
1748+
Some(RegistryType::Terraform) => icons::TERRAFORM,
1749+
Some(RegistryType::Ansible) => icons::ANSIBLE,
1750+
Some(RegistryType::Nuget) => icons::NUGET,
1751+
Some(RegistryType::PubDart) => icons::PUB,
1752+
Some(RegistryType::Conan) => icons::CONAN,
1753+
Some(RegistryType::Rpm) => icons::RPM,
1754+
Some(RegistryType::Deb) => icons::DEB,
1755+
None => {
17521756
r#"<path fill="currentColor" d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>"#
17531757
}
17541758
}
17551759
}
17561760

17571761
fn get_registry_title(registry_type: &str) -> &'static str {
1758-
match registry_type {
1759-
"docker" => "Docker Registry",
1760-
"maven" => "Maven Repository",
1761-
"npm" => "npm Registry",
1762-
"cargo" => "Cargo Registry",
1763-
"pypi" => "PyPI Repository",
1764-
"go" => "Go Modules",
1765-
"raw" => "Raw Storage",
1766-
"gems" => "RubyGems",
1767-
"terraform" => "Terraform Registry",
1768-
"ansible" => "Ansible Galaxy",
1769-
"nuget" => "NuGet Gallery",
1770-
"pub" => "pub.dev",
1771-
"conan" => "Conan (C/C++)",
1772-
"rpm" => "RPM (yum/dnf)",
1773-
"deb" => "Debian (APT)",
1774-
_ => "Registry",
1762+
match RegistryType::from_str_opt(registry_type) {
1763+
Some(RegistryType::Docker) => "Docker Registry",
1764+
Some(RegistryType::Maven) => "Maven Repository",
1765+
Some(RegistryType::Npm) => "npm Registry",
1766+
Some(RegistryType::Cargo) => "Cargo Registry",
1767+
Some(RegistryType::PyPI) => "PyPI Repository",
1768+
Some(RegistryType::Go) => "Go Modules",
1769+
Some(RegistryType::Raw) => "Raw Storage",
1770+
Some(RegistryType::Gems) => "RubyGems",
1771+
Some(RegistryType::Terraform) => "Terraform Registry",
1772+
Some(RegistryType::Ansible) => "Ansible Galaxy",
1773+
Some(RegistryType::Nuget) => "NuGet Gallery",
1774+
Some(RegistryType::PubDart) => "pub.dev",
1775+
Some(RegistryType::Conan) => "Conan (C/C++)",
1776+
Some(RegistryType::Rpm) => "RPM (yum/dnf)",
1777+
Some(RegistryType::Deb) => "Debian (APT)",
1778+
None => "Registry",
17751779
}
17761780
}
17771781

0 commit comments

Comments
 (0)