Files
snack/rpm/parse.go
Tai Groot 2685dd945c feat: add dnf and rpm package manager implementations
Implements the dnf sub-package with Manager, VersionQuerier, Holder,
Cleaner, FileOwner, RepoManager, KeyManager, Grouper, and NameNormalizer
interfaces.

Implements the rpm sub-package with Manager, FileOwner, and
NameNormalizer interfaces.

Both follow the existing pattern: exported methods on struct delegate to
unexported functions, _linux.go for real implementations, _other.go with
build-tag stubs, embedded snack.Locker for mutating operations, and
compile-time interface checks.

Includes parser tests for all output formats.
2026-02-25 22:30:16 +00:00

98 lines
2.0 KiB
Go

package rpm
import (
"strings"
"github.com/gogrlx/snack"
)
// knownArchs is the set of RPM architecture suffixes.
var knownArchs = map[string]bool{
"x86_64": true,
"i686": true,
"i386": true,
"aarch64": true,
"armv7hl": true,
"ppc64le": true,
"s390x": true,
"noarch": true,
"src": true,
}
// normalizeName strips architecture suffixes from a package name.
func normalizeName(name string) string {
n, _ := parseArchSuffix(name)
return n
}
// parseArchSuffix extracts the architecture from a package name if present.
func parseArchSuffix(name string) (string, string) {
idx := strings.LastIndex(name, ".")
if idx < 0 {
return name, ""
}
arch := name[idx+1:]
if knownArchs[arch] {
return name[:idx], arch
}
return name, ""
}
// parseList parses rpm -qa --queryformat output.
// Format: "NAME\tVERSION-RELEASE\tSUMMARY\n"
func parseList(output string) []snack.Package {
var pkgs []snack.Package
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, "\t", 3)
if len(parts) < 2 {
continue
}
pkg := snack.Package{
Name: parts[0],
Version: parts[1],
Installed: true,
}
if len(parts) >= 3 {
pkg.Description = parts[2]
}
pkgs = append(pkgs, pkg)
}
return pkgs
}
// parseInfo parses rpm -qi output.
// Format is "Key : Value" lines.
func parseInfo(output string) *snack.Package {
pkg := &snack.Package{}
for _, line := range strings.Split(output, "\n") {
idx := strings.Index(line, ":")
if idx < 0 {
continue
}
key := strings.TrimSpace(line[:idx])
val := strings.TrimSpace(line[idx+1:])
switch key {
case "Name":
pkg.Name = val
case "Version":
pkg.Version = val
case "Release":
if pkg.Version != "" {
pkg.Version = pkg.Version + "-" + val
}
case "Architecture", "Arch":
pkg.Arch = val
case "Summary":
pkg.Description = val
}
}
if pkg.Name == "" {
return nil
}
return pkg
}