From 34e8d4676f152dcf4b81d0a39b973384cfefeeaa Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Wed, 18 Mar 2026 19:56:50 +0000 Subject: [PATCH] Fix golangci-lint goconst, mnd, and gocognit warnings Extract repeated string literals into named constants, replace magic numbers in SplitN calls with named constants, and reduce cognitive complexity of compose() by extracting per-ecosystem helpers. --- data.go | 4 +- format.go | 167 +++++++++++++++++++++++++--------------------- parse.go | 101 +++++++++++++++------------- translate_test.go | 2 +- 4 files changed, 149 insertions(+), 125 deletions(-) diff --git a/data.go b/data.go index c737bf2..10ba2af 100644 --- a/data.go +++ b/data.go @@ -16,6 +16,8 @@ var osesJSON []byte //go:embed data/platforms.json var platformsJSON []byte +const jsonNull = "null" + // rawMapping is a JSON value that can be a string, array of strings, or null. type rawMapping = json.RawMessage @@ -90,7 +92,7 @@ func loadData() (*indices, error) { // resolveMapping extracts the string value(s) from a raw JSON mapping. // Returns the preferred (first) value and all values. func resolveMapping(raw rawMapping) (preferred string, all []string) { - if raw == nil || string(raw) == "null" { + if raw == nil || string(raw) == jsonNull { return "", nil } diff --git a/format.go b/format.go index 9d1ff20..b1d1b8b 100644 --- a/format.go +++ b/format.go @@ -5,6 +5,21 @@ import ( "strings" ) +// Common platform string constants used across formatting and parsing. +const ( + osLinux = "linux" + osDarwin = "darwin" + osWindows = "windows" + + abiGNU = "gnu" + abiMusl = "musl" + abiMSVC = "msvc" + abiEABI = "eabi" + abiEABIHF = "eabihf" + abiGNUEABI = "gnueabi" + abiGNUEABIHF = "gnueabihf" +) + // Format converts a canonical Platform into an ecosystem-specific string. func Format(eco Ecosystem, p Platform) (string, error) { if !validEcosystem(eco) { @@ -72,74 +87,25 @@ func compose(idx *indices, eco Ecosystem, p Platform) (string, error) { case Node: return osName + "-" + arch, nil case Rust: - vendor := p.Vendor - if vendor == "" { - vendor = defaultVendor(p.OS) - } - abi := p.ABI - if abi == "" && p.OS == "linux" { - abi = "gnu" - } - if abi != "" { - return arch + "-" + vendor + "-" + osName + "-" + rustABI(abi), nil - } - return arch + "-" + vendor + "-" + osName, nil + return composeTriple(arch, osName, p, rustABI), nil case RubyGems: - if p.ABI != "" && p.ABI != "gnu" { - return arch + "-" + osName + "-" + p.ABI, nil - } - return arch + "-" + osName, nil + return composeRubyGems(arch, osName, p), nil case Python: return composePython(arch, osName, p), nil case Debian: - abi := p.ABI - if abi == "" && p.OS == "linux" { - abi = "gnu" - } - if abi == "" { - return "", &ErrNoMapping{Ecosystem: eco, Platform: p} - } - return arch + "-" + osName + "-" + debianABI(abi), nil + return composeDebian(arch, osName, eco, p) case LLVM: - vendor := p.Vendor - if vendor == "" { - vendor = defaultVendor(p.OS) - } - abi := p.ABI - if abi == "" && p.OS == "linux" { - abi = "gnu" - } - if abi != "" { - return arch + "-" + vendor + "-" + osName + "-" + abi, nil - } - return arch + "-" + vendor + "-" + osName, nil + return composeTriple(arch, osName, p, identityABI), nil case NuGet: - if p.ABI == "musl" { - return osName + "-musl-" + arch, nil - } - return osName + "-" + arch, nil + return composeNuGet(arch, osName, p), nil case Vcpkg: return arch + "-" + osName, nil case Conan: return osName + "/" + arch, nil case Homebrew: - if p.OS != "darwin" { - return "", &ErrNoMapping{Ecosystem: eco, Platform: p} - } - return arch + "_darwin", nil + return composeHomebrew(arch, eco, p) case Swift: - vendor := p.Vendor - if vendor == "" { - vendor = defaultVendor(p.OS) - } - abi := p.ABI - if abi == "" && p.OS == "linux" { - abi = "gnu" - } - if abi != "" { - return arch + "-" + vendor + "-" + osName + "-" + abi, nil - } - return arch + "-" + vendor + "-" + osName, nil + return composeTriple(arch, osName, p, identityABI), nil case Kotlin: return osName + arch, nil case Maven: @@ -148,11 +114,60 @@ func compose(idx *indices, eco Ecosystem, p Platform) (string, error) { return "", &ErrNoMapping{Ecosystem: eco, Platform: p} } +func composeTriple(arch, osName string, p Platform, abiFn func(string) string) string { + vendor := p.Vendor + if vendor == "" { + vendor = defaultVendor(p.OS) + } + abi := p.ABI + if abi == "" && p.OS == osLinux { + abi = abiGNU + } + if abi != "" { + return arch + "-" + vendor + "-" + osName + "-" + abiFn(abi) + } + return arch + "-" + vendor + "-" + osName +} + +func identityABI(abi string) string { return abi } + +func composeRubyGems(arch, osName string, p Platform) string { + if p.ABI != "" && p.ABI != abiGNU { + return arch + "-" + osName + "-" + p.ABI + } + return arch + "-" + osName +} + +func composeDebian(arch, osName string, eco Ecosystem, p Platform) (string, error) { + abi := p.ABI + if abi == "" && p.OS == osLinux { + abi = abiGNU + } + if abi == "" { + return "", &ErrNoMapping{Ecosystem: eco, Platform: p} + } + return arch + "-" + osName + "-" + debianABI(abi), nil +} + +func composeNuGet(arch, osName string, p Platform) string { + if p.ABI == abiMusl { + return osName + "-musl-" + arch + } + return osName + "-" + arch +} + +func composeHomebrew(arch string, eco Ecosystem, p Platform) (string, error) { + if p.OS != osDarwin { + return "", &ErrNoMapping{Ecosystem: eco, Platform: p} + } + return arch + "_darwin", nil +} + func defaultVendor(os string) string { switch os { - case "darwin", "ios": + case osDarwin, "ios": return "apple" - case "windows": + case osWindows: return "pc" default: return "unknown" @@ -161,10 +176,10 @@ func defaultVendor(os string) string { func defaultABI(os string) string { switch os { - case "linux": - return "gnu" - case "windows": - return "msvc" + case osLinux: + return abiGNU + case osWindows: + return abiMSVC default: return "" } @@ -172,10 +187,10 @@ func defaultABI(os string) string { func rustABI(abi string) string { switch abi { - case "eabihf": - return "gnueabihf" - case "eabi": - return "gnueabi" + case abiEABIHF: + return abiGNUEABIHF + case abiEABI: + return abiGNUEABI default: return abi } @@ -183,12 +198,12 @@ func rustABI(abi string) string { func debianABI(abi string) string { switch abi { - case "eabihf": - return "gnueabihf" - case "eabi": - return "gnueabi" - case "gnu": - return "gnu" + case abiEABIHF: + return abiGNUEABIHF + case abiEABI: + return abiGNUEABI + case abiGNU: + return abiGNU default: return abi } @@ -196,7 +211,7 @@ func debianABI(abi string) string { func composePython(arch, osName string, p Platform) string { switch p.OS { - case "darwin": + case osDarwin: ver := p.OSVersion if ver == "" { if p.Arch == "aarch64" { @@ -207,8 +222,8 @@ func composePython(arch, osName string, p Platform) string { } verParts := underscoreVersion(ver) return "macosx_" + verParts + "_" + arch - case "linux": - if p.ABI == "musl" { + case osLinux: + if p.ABI == abiMusl { ver := p.LibCVersion if ver == "" { ver = "1.1" @@ -222,7 +237,7 @@ func composePython(arch, osName string, p Platform) string { } verParts := underscoreVersion(ver) return "manylinux_" + verParts + "_" + arch - case "windows": + case osWindows: if p.Arch == "i686" { return "win32" } diff --git a/parse.go b/parse.go index 85d25a8..1e0dda6 100644 --- a/parse.go +++ b/parse.go @@ -6,6 +6,13 @@ import ( "strings" ) +// Split limits for strings.SplitN in decompose functions. +const ( + splitTwo = 2 + splitThree = 3 + splitFour = 4 +) + var ( // manylinux_2_17_x86_64, musllinux_1_1_aarch64 reManylinux = regexp.MustCompile(`^(many|musl)linux_(\d+)_(\d+)_(\w+)$`) @@ -91,8 +98,8 @@ func decompose(idx *indices, eco Ecosystem, s string) (Platform, bool) { // go: os/arch func decomposeGo(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "/", 2) - if len(parts) != 2 { + parts := strings.SplitN(s, "/", splitTwo) + if len(parts) != splitTwo { return Platform{}, false } osName := resolveOS(idx, Go, parts[0]) @@ -105,8 +112,8 @@ func decomposeGo(idx *indices, s string) (Platform, bool) { // node: os-arch func decomposeNode(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 2) - if len(parts) != 2 { + parts := strings.SplitN(s, "-", splitTwo) + if len(parts) != splitTwo { return Platform{}, false } osName := resolveOS(idx, Node, parts[0]) @@ -119,8 +126,8 @@ func decomposeNode(idx *indices, s string) (Platform, bool) { // rust/llvm: arch-vendor-os[-abi] func decomposeRustLLVM(idx *indices, eco Ecosystem, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 4) - if len(parts) < 3 { + parts := strings.SplitN(s, "-", splitFour) + if len(parts) < splitThree { return Platform{}, false } arch := resolveArch(idx, eco, parts[0]) @@ -133,7 +140,7 @@ func decomposeRustLLVM(idx *indices, eco Ecosystem, s string) (Platform, bool) { return Platform{}, false } p := Platform{Arch: arch, OS: osName, Vendor: vendor} - if len(parts) == 4 { + if len(parts) == splitFour { p.ABI = normalizeABI(parts[3]) } return p, true @@ -141,8 +148,8 @@ func decomposeRustLLVM(idx *indices, eco Ecosystem, s string) (Platform, bool) { // rubygems: arch-os[-abi] or cpu-os[-version] func decomposeRubyGems(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 3) - if len(parts) < 2 { + parts := strings.SplitN(s, "-", splitThree) + if len(parts) < splitTwo { return Platform{}, false } arch := resolveArch(idx, RubyGems, parts[0]) @@ -154,7 +161,7 @@ func decomposeRubyGems(idx *indices, s string) (Platform, bool) { return Platform{}, false } p := Platform{Arch: arch, OS: osName} - if len(parts) == 3 { + if len(parts) == splitThree { p.ABI = normalizeABI(parts[2]) } return p, true @@ -167,13 +174,13 @@ func decomposePython(idx *indices, s string) (Platform, bool) { if arch == "" { return Platform{}, false } - abi := "gnu" - if m[1] == "musl" { - abi = "musl" + abi := abiGNU + if m[1] == abiMusl { + abi = abiMusl } return Platform{ Arch: arch, - OS: "linux", + OS: osLinux, ABI: abi, LibCVersion: m[2] + "." + m[3], }, true @@ -186,14 +193,14 @@ func decomposePython(idx *indices, s string) (Platform, bool) { } return Platform{ Arch: arch, - OS: "darwin", + OS: osDarwin, Vendor: "apple", OSVersion: m[1] + "." + m[2], }, true } if s == "win32" { - return Platform{Arch: "i686", OS: "windows", Vendor: "pc"}, true + return Platform{Arch: "i686", OS: osWindows, Vendor: "pc"}, true } if m := reWinPython.FindStringSubmatch(s); m != nil && m[1] != "" { @@ -201,7 +208,7 @@ func decomposePython(idx *indices, s string) (Platform, bool) { if arch == "" { return Platform{}, false } - return Platform{Arch: arch, OS: "windows", Vendor: "pc"}, true + return Platform{Arch: arch, OS: osWindows, Vendor: "pc"}, true } if m := reLinuxPython.FindStringSubmatch(s); m != nil { @@ -209,7 +216,7 @@ func decomposePython(idx *indices, s string) (Platform, bool) { if arch == "" { return Platform{}, false } - return Platform{Arch: arch, OS: "linux"}, true + return Platform{Arch: arch, OS: osLinux}, true } return Platform{}, false @@ -217,8 +224,8 @@ func decomposePython(idx *indices, s string) (Platform, bool) { // debian: arch-os-abi func decomposeDebian(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 3) - if len(parts) != 3 { + parts := strings.SplitN(s, "-", splitThree) + if len(parts) != splitThree { return Platform{}, false } arch := resolveArch(idx, Debian, parts[0]) @@ -235,20 +242,20 @@ func decomposeDebian(idx *indices, s string) (Platform, bool) { func normalizeABI(s string) string { s = strings.ToLower(s) switch { - case s == "gnu" || s == "gnueabi": - return "gnu" - case s == "gnueabihf": - return "eabihf" - case s == "musl": - return "musl" - case s == "msvc": - return "msvc" + case s == abiGNU || s == abiGNUEABI: + return abiGNU + case s == abiGNUEABIHF: + return abiEABIHF + case s == abiMusl: + return abiMusl + case s == abiMSVC: + return abiMSVC case strings.HasPrefix(s, "mingw"): return "mingw" - case s == "eabi": - return "eabi" - case s == "eabihf": - return "eabihf" + case s == abiEABI: + return abiEABI + case s == abiEABIHF: + return abiEABIHF } return s } @@ -263,16 +270,16 @@ func parsePythonVersions(s string, p *Platform) { // nuget: os-arch or os-musl-arch (e.g., linux-x64, linux-musl-x64, win-arm64, osx-x64) func decomposeNuGet(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 3) - if len(parts) == 3 && strings.ToLower(parts[1]) == "musl" { + parts := strings.SplitN(s, "-", splitThree) + if len(parts) == splitThree && strings.ToLower(parts[1]) == abiMusl { osName := resolveOS(idx, NuGet, parts[0]) arch := resolveArch(idx, NuGet, parts[2]) if osName == "" || arch == "" { return Platform{}, false } - return Platform{Arch: arch, OS: osName, ABI: "musl"}, true + return Platform{Arch: arch, OS: osName, ABI: abiMusl}, true } - if len(parts) < 2 { + if len(parts) < splitTwo { return Platform{}, false } osName := resolveOS(idx, NuGet, parts[0]) @@ -285,8 +292,8 @@ func decomposeNuGet(idx *indices, s string) (Platform, bool) { // vcpkg: arch-os (e.g., x64-linux, arm64-osx, x64-windows) func decomposeVcpkg(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 2) - if len(parts) != 2 { + parts := strings.SplitN(s, "-", splitTwo) + if len(parts) != splitTwo { return Platform{}, false } arch := resolveArch(idx, Vcpkg, parts[0]) @@ -301,8 +308,8 @@ func decomposeVcpkg(idx *indices, s string) (Platform, bool) { // Conan uses settings like os=Linux, arch=armv8 but we parse "os/arch" pairs func decomposeConan(idx *indices, s string) (Platform, bool) { // Try os/arch with slash separator - parts := strings.SplitN(s, "/", 2) - if len(parts) == 2 { + parts := strings.SplitN(s, "/", splitTwo) + if len(parts) == splitTwo { osName := resolveOS(idx, Conan, parts[0]) arch := resolveArch(idx, Conan, parts[1]) if osName != "" && arch != "" { @@ -310,8 +317,8 @@ func decomposeConan(idx *indices, s string) (Platform, bool) { } } // Try os-arch with dash separator - parts = strings.SplitN(s, "-", 2) - if len(parts) == 2 { + parts = strings.SplitN(s, "-", splitTwo) + if len(parts) == splitTwo { osName := resolveOS(idx, Conan, parts[0]) arch := resolveArch(idx, Conan, parts[1]) if osName != "" && arch != "" { @@ -324,8 +331,8 @@ func decomposeConan(idx *indices, s string) (Platform, bool) { // homebrew: arch_codename (e.g., arm64_sonoma, arm64_ventura) // We can only extract the arch since the codename maps to a macOS version, not a canonical OS func decomposeHomebrew(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "_", 2) - if len(parts) != 2 { + parts := strings.SplitN(s, "_", splitTwo) + if len(parts) != splitTwo { return Platform{}, false } arch := resolveArch(idx, Homebrew, parts[0]) @@ -333,7 +340,7 @@ func decomposeHomebrew(idx *indices, s string) (Platform, bool) { return Platform{}, false } // Homebrew is macOS-only - return Platform{Arch: arch, OS: "darwin", Vendor: "apple"}, true + return Platform{Arch: arch, OS: osDarwin, Vendor: "apple"}, true } // kotlin: camelCase DSL names (e.g., linuxX64, macosArm64, mingwX64, iosArm64, androidNativeArm64) @@ -354,8 +361,8 @@ func decomposeKotlin(idx *indices, s string) (Platform, bool) { // maven: os-arch (e.g., linux-x86_64, osx-aarch_64, windows-x86_64) func decomposeMaven(idx *indices, s string) (Platform, bool) { - parts := strings.SplitN(s, "-", 2) - if len(parts) != 2 { + parts := strings.SplitN(s, "-", splitTwo) + if len(parts) != splitTwo { return Platform{}, false } osName := resolveOS(idx, Maven, parts[0]) diff --git a/translate_test.go b/translate_test.go index 9cb6dd3..800d4e9 100644 --- a/translate_test.go +++ b/translate_test.go @@ -136,7 +136,7 @@ func TestTranslateRoundTrip(t *testing.T) { if p.Arch != "x86_64" { t.Errorf("round-trip Arch = %q, want x86_64", p.Arch) } - if p.OS != "linux" { + if p.OS != osLinux { t.Errorf("round-trip OS = %q, want linux", p.OS) } })