package rpm import "io/fs" import "path/filepath" import "sort" import "strings" import repokit "repokit" type PackageSummary struct { Filename string `json:"filename"` Name string `json:"name"` Version string `json:"version"` Release string `json:"release"` Arch string `json:"arch"` Summary string `json:"summary"` } type PackageDetail struct { PackageSummary Description string `json:"description"` License string `json:"license"` URL string `json:"url"` BuildTime int64 `json:"build_time"` Size int64 `json:"size"` Files []string `json:"files"` Requires []string `json:"requires"` Provides []string `json:"provides"` Changelogs []PackageChangeLog `json:"changelogs"` } type PackageChangeLog struct { Author string `json:"author"` Date int64 `json:"date"` Text string `json:"text"` } func ListPackages(repoPath string) ([]PackageSummary, error) { var packages []PackageSummary var walkErr error var err error walkErr = filepath.WalkDir(repoPath, func(path string, entry fs.DirEntry, entryErr error) error { var lower string var rel string var pkg *repokit.RpmPackage var summary PackageSummary if entryErr != nil { return entryErr } if entry.IsDir() { return nil } lower = strings.ToLower(entry.Name()) if !strings.HasSuffix(lower, ".rpm") { return nil } rel, err = filepath.Rel(repoPath, path) if err != nil { return err } pkg, err = repokit.RpmPackageFromRpmBase(path, 0) if err != nil { return nil } summary = packageSummaryFromRepokit(pkg, filepath.ToSlash(rel)) packages = append(packages, summary) return nil }) if walkErr != nil { return nil, walkErr } sort.Slice(packages, func(i int, j int) bool { if packages[i].Name == packages[j].Name { return packages[i].Filename < packages[j].Filename } return packages[i].Name < packages[j].Name }) return packages, nil } func GetPackageDetail(repoPath string, filename string) (PackageDetail, error) { var detail PackageDetail var fullPath string var pkg *repokit.RpmPackage var err error fullPath = filepath.Join(repoPath, filepath.FromSlash(filename)) pkg, err = repokit.RpmPackageFromRpmBase(fullPath, 256) if err != nil { return detail, err } detail = packageDetailFromRepokit(pkg, filename) return detail, nil } func packageSummaryFromRepokit(pkg *repokit.RpmPackage, filename string) PackageSummary { var summary PackageSummary summary = PackageSummary{ Filename: filename, Name: strings.TrimSpace(pkg.Name), Version: strings.TrimSpace(pkg.Version), Release: strings.TrimSpace(pkg.Release), Arch: strings.TrimSpace(pkg.Arch), Summary: strings.TrimSpace(pkg.Summary), } return summary } func packageDetailFromRepokit(pkg *repokit.RpmPackage, filename string) PackageDetail { var files []string var file repokit.RpmPackageFile var changelogs []PackageChangeLog var changelog repokit.RpmChangelogEntry var detail PackageDetail files = make([]string, 0, len(pkg.Files)) for _, file = range pkg.Files { if file.FullPath == "" { continue } files = append(files, file.FullPath) } changelogs = make([]PackageChangeLog, 0, len(pkg.Changelogs)) for _, changelog = range pkg.Changelogs { changelogs = append(changelogs, PackageChangeLog{ Author: strings.TrimSpace(changelog.Author), Date: changelog.Date, Text: strings.TrimSpace(changelog.Changelog), }) } sort.SliceStable(changelogs, func(i int, j int) bool { return changelogs[i].Date > changelogs[j].Date }) sort.Strings(files) detail = PackageDetail{ PackageSummary: packageSummaryFromRepokit(pkg, filename), Description: strings.TrimSpace(pkg.Description), License: strings.TrimSpace(pkg.RpmLicense), URL: strings.TrimSpace(pkg.Url), BuildTime: pkg.TimeBuild, Size: pkg.SizePackage, Files: files, Requires: dependencyListToStrings(pkg.Requires), Provides: dependencyListToStrings(pkg.Provides), Changelogs: changelogs, } return detail } func dependencyListToStrings(deps []repokit.RpmDependency) []string { var lines []string var dep repokit.RpmDependency lines = make([]string, 0, len(deps)) for _, dep = range deps { lines = append(lines, dependencyToString(dep)) } return lines } func dependencyToString(dep repokit.RpmDependency) string { var op string var evr string var line string op = normalizeDependencyOp(dep.Flags) evr = dependencyEVR(dep) line = dep.Name if op == "" || evr == "" { return line } line = line + " " + op + " " + evr return line } func normalizeDependencyOp(flag string) string { switch strings.ToUpper(strings.TrimSpace(flag)) { case "LT": return "<" case "GT": return ">" case "EQ": return "=" case "LE": return "<=" case "GE": return ">=" default: return strings.TrimSpace(flag) } } func dependencyEVR(dep repokit.RpmDependency) string { var value string var version string version = strings.TrimSpace(dep.Version) if version == "" { return "" } value = version if strings.TrimSpace(dep.Release) != "" { value = value + "-" + strings.TrimSpace(dep.Release) } if strings.TrimSpace(dep.Epoch) != "" && strings.TrimSpace(dep.Epoch) != "0" { value = strings.TrimSpace(dep.Epoch) + ":" + value } return value }