Files
luet/vendor/github.com/Sabayon/pkgs-checker/pkg/gentoo/pkg.go
2020-03-10 09:12:22 +01:00

457 lines
11 KiB
Go

/*
Copyright (C) 2017-2019 Daniele Rondina <geaaru@sabayonlinux.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gentoo
import (
"errors"
"fmt"
"regexp"
"strings"
version "github.com/hashicorp/go-version"
)
// ----------------------------------
// Code to move and merge inside luet project
// ----------------------------------
// Package condition
type PackageCond int
const (
PkgCondInvalid = 0
// >
PkgCondGreater = 1
// >=
PkgCondGreaterEqual = 2
// <
PkgCondLess = 3
// <=
PkgCondLessEqual = 4
// =
PkgCondEqual = 5
// !
PkgCondNot = 6
// ~
PkgCondAnyRevision = 7
// =<pkg>*
PkgCondMatchVersion = 8
)
const (
RegexCatString = `(^[a-z]+[0-9]*[a-z]*[-][a-z]+[0-9]*[a-z]*|^virtual)`
RegexPkgNameString = `([a-z]+[0-9a-zA-Z\.\-_[+]*]*[+]*)`
)
type GentooPackage struct {
Name string `json:"name",omitempty"`
Category string `json:"category",omitempty"`
Version string `json:"version",omitempty"`
VersionSuffix string `json:"version_suffix",omitempty"`
VersionBuild string `json:"version_build",omitempty"`
Slot string `json:"slot",omitempty"`
Condition PackageCond
Repository string `json:"repository",omitempty"`
UseFlags []string `json:"use_flags",omitempty"`
}
func (p *GentooPackage) String() string {
// TODO
opt := ""
if p.Version != "" {
opt = "-"
}
return fmt.Sprintf("%s/%s%s%s%s",
p.Category, p.Name, opt,
p.Version, p.VersionSuffix)
}
func (p PackageCond) String() (ans string) {
if p == PkgCondInvalid {
ans = ""
} else if p == PkgCondGreater {
ans = ">"
} else if p == PkgCondGreaterEqual {
ans = ">="
} else if p == PkgCondLess {
ans = "<"
} else if p == PkgCondLessEqual {
ans = "<="
} else if p == PkgCondEqual {
ans = "="
} else if p == PkgCondNot {
ans = "!"
} else if p == PkgCondAnyRevision {
ans = "~"
} else if p == PkgCondMatchVersion {
ans = "=*"
}
return ans
}
func (p PackageCond) Int() (ans int) {
if p == PkgCondInvalid {
ans = PkgCondInvalid
} else if p == PkgCondGreater {
ans = PkgCondGreater
} else if p == PkgCondGreaterEqual {
ans = PkgCondGreaterEqual
} else if p == PkgCondLess {
ans = PkgCondLess
} else if p == PkgCondLessEqual {
ans = PkgCondLessEqual
} else if p == PkgCondEqual {
// To permit correct matching on database
// we currently use directly package version without =
ans = PkgCondEqual
} else if p == PkgCondNot {
ans = PkgCondNot
} else if p == PkgCondAnyRevision {
ans = PkgCondAnyRevision
} else if p == PkgCondMatchVersion {
ans = PkgCondMatchVersion
}
return
}
func sanitizeVersion(v string) string {
// https://devmanual.gentoo.org/ebuild-writing/file-format/index.html
ans := strings.ReplaceAll(v, "_alpha", "-alpha")
ans = strings.ReplaceAll(ans, "_beta", "-beta")
ans = strings.ReplaceAll(ans, "_pre", "-pre")
ans = strings.ReplaceAll(ans, "_rc", "-rc")
ans = strings.ReplaceAll(ans, "_p", "-p")
return ans
}
func (p *GentooPackage) OfPackage(i *GentooPackage) (ans bool) {
if p.Category == i.Category && p.Name == i.Name {
ans = true
} else {
ans = false
}
return
}
func (p *GentooPackage) GetPackageName() (ans string) {
ans = fmt.Sprintf("%s/%s", p.Category, p.Name)
return
}
func (p *GentooPackage) GetP() string {
return fmt.Sprintf("%s-%s", p.Name, p.GetPV())
}
func (p *GentooPackage) GetPN() string {
return p.Name
}
func (p *GentooPackage) GetPV() string {
return fmt.Sprintf("%s", p.Version)
}
func (p *GentooPackage) GetPVR() (ans string) {
if p.VersionSuffix != "" {
ans = fmt.Sprintf("%s%s", p.Version, p.VersionSuffix)
} else {
ans = p.GetPV()
}
return
}
func (p *GentooPackage) GetPF() string {
return fmt.Sprintf("%s-%s", p.GetPN(), p.GetPVR())
}
func (p *GentooPackage) Admit(i *GentooPackage) (bool, error) {
var ans bool = false
var v1 *version.Version = nil
var v2 *version.Version = nil
var err error
if p.Category != i.Category {
return false, errors.New(
fmt.Sprintf("Wrong category for package %s", i.Name))
}
if p.Name != i.Name {
return false, errors.New(
fmt.Sprintf("Wrong name for package %s", i.Name))
}
v1s := p.Version
v2s := i.Version
if v1s != "" {
if p.VersionBuild != "" {
v1s = p.Version + "+" + p.VersionBuild
}
v1, err = version.NewVersion(v1s)
if err != nil {
return false, err
}
}
if v2s != "" {
if i.VersionBuild != "" {
v2s = i.Version + "+" + i.VersionBuild
}
v2, err = version.NewVersion(v2s)
if err != nil {
return false, err
}
}
// If package doesn't define version admit all versions of the package.
if p.Version == "" {
ans = true
} else {
if p.Condition == PkgCondInvalid || p.Condition == PkgCondEqual {
// case 1: source-pkg-1.0 and dest-pkg-1.0 or dest-pkg without version
if i.Version != "" && i.Version == p.Version && p.VersionSuffix == i.VersionSuffix &&
p.VersionBuild == i.VersionBuild {
ans = true
}
} else if p.Condition == PkgCondAnyRevision {
if v1 != nil && v2 != nil {
ans = v1.Equal(v2)
}
} else if p.Condition == PkgCondMatchVersion {
// TODO: case of 7.3* where 7.30 is accepted.
if v1 != nil && v2 != nil {
segments := v1.Segments()
n := strings.Count(p.Version, ".")
switch n {
case 0:
segments[0]++
case 1:
segments[1]++
case 2:
segments[2]++
default:
segments[len(segments)-1]++
}
nextVersion := strings.Trim(strings.Replace(fmt.Sprint(segments), " ", ".", -1), "[]")
constraints, err := version.NewConstraint(
fmt.Sprintf(">= %s, < %s", p.Version, nextVersion),
)
if err != nil {
return false, err
}
ans = constraints.Check(v2)
}
} else if v1 != nil && v2 != nil {
switch p.Condition {
case PkgCondGreaterEqual:
ans = v2.GreaterThanOrEqual(v1)
case PkgCondLessEqual:
ans = v2.LessThanOrEqual(v1)
case PkgCondGreater:
ans = v2.GreaterThan(v1)
case PkgCondLess:
ans = v2.LessThan(v1)
case PkgCondNot:
ans = !v2.Equal(v1)
}
}
}
return ans, nil
}
// return category, package, version, slot, condition
func ParsePackageStr(pkg string) (*GentooPackage, error) {
if pkg == "" {
return nil, errors.New("Invalid package string")
}
ans := GentooPackage{
Slot: "0",
Condition: PkgCondInvalid,
VersionBuild: "",
}
// Check if pkg string contains inline use flags
regexUses := regexp.MustCompile(
"\\[([a-z]*[-]*[0-9]*[,]*)+\\]*$",
)
mUses := regexUses.FindAllString(pkg, -1)
if len(mUses) > 0 {
ans.UseFlags = strings.Split(
pkg[len(pkg)-len(mUses[0])+1:len(pkg)-1],
",",
)
pkg = pkg[:len(pkg)-len(mUses[0])]
}
if strings.HasPrefix(pkg, ">=") {
pkg = pkg[2:]
ans.Condition = PkgCondGreaterEqual
} else if strings.HasPrefix(pkg, ">") {
pkg = pkg[1:]
ans.Condition = PkgCondGreater
} else if strings.HasPrefix(pkg, "<=") {
pkg = pkg[2:]
ans.Condition = PkgCondLessEqual
} else if strings.HasPrefix(pkg, "<") {
pkg = pkg[1:]
ans.Condition = PkgCondLess
} else if strings.HasPrefix(pkg, "=") {
pkg = pkg[1:]
if strings.HasSuffix(pkg, "*") {
ans.Condition = PkgCondMatchVersion
pkg = pkg[0 : len(pkg)-1]
} else {
ans.Condition = PkgCondEqual
}
} else if strings.HasPrefix(pkg, "~") {
pkg = pkg[1:]
ans.Condition = PkgCondAnyRevision
} else if strings.HasPrefix(pkg, "!") {
pkg = pkg[1:]
ans.Condition = PkgCondNot
}
regexVerString := fmt.Sprintf("[-](%s|%s|%s|%s|%s|%s)((%s|%s|%s|%s|%s|%s|%s)+)*",
// Version regex
// 1.1
"[0-9]+[.][0-9]+[a-z]*",
// 1
"[0-9]+[a-z]*",
// 1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// suffix
"-r[0-9]+",
"_p[0-9]+",
"_pre[0-9]*",
"_rc[0-9]+",
// handle also rc without number
"_rc",
"_alpha",
"_beta",
)
hasBuild, _ := regexp.MatchString(
fmt.Sprintf("(%s[/]%s%s([[:]{1,2}[0-9a-zA-Z]*]*)*[+])",
RegexCatString, RegexPkgNameString, regexVerString),
pkg,
)
if hasBuild {
// Check if build number is present
buildIdx := strings.LastIndex(pkg, "+")
if buildIdx > 0 {
// <pre-release> ::= <dot-separated pre-release identifiers>
//
// <dot-separated pre-release identifiers> ::=
// <pre-release identifier> | <pre-release identifier> "."
// <dot-separated pre-release identifiers>
//
// <build> ::= <dot-separated build identifiers>
//
// <dot-separated build identifiers> ::= <build identifier>
// | <build identifier> "." <dot-separated build identifiers>
//
// <pre-release identifier> ::= <alphanumeric identifier>
// | <numeric identifier>
//
// <build identifier> ::= <alphanumeric identifier>
// | <digits>
//
// <alphanumeric identifier> ::= <non-digit>
// | <non-digit> <identifier characters>
// | <identifier characters> <non-digit>
// | <identifier characters> <non-digit> <identifier characters>
ans.VersionBuild = pkg[buildIdx+1:]
pkg = pkg[0:buildIdx]
}
}
words := strings.Split(pkg, "/")
if len(words) != 2 {
return nil, errors.New(fmt.Sprintf("Invalid package string %s", pkg))
}
ans.Category = words[0]
pkgname := words[1]
// Check if has repository
if strings.Contains(pkgname, "::") {
words = strings.Split(pkgname, "::")
ans.Repository = words[1]
pkgname = words[0]
}
// Check if has slot
if strings.Contains(pkgname, ":") {
words = strings.Split(pkgname, ":")
ans.Slot = words[1]
pkgname = words[0]
}
regexPkg := regexp.MustCompile(
fmt.Sprintf("%s$", regexVerString),
)
matches := regexPkg.FindAllString(pkgname, -1)
// NOTE: Now suffix comples like _alpha_rc1 are not supported.
if len(matches) > 0 {
// Check if there patch
if strings.Contains(matches[0], "_p") {
ans.Version = matches[0][1:strings.Index(matches[0], "_p")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_p"):]
} else if strings.Contains(matches[0], "_rc") {
ans.Version = matches[0][1:strings.Index(matches[0], "_rc")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_rc"):]
} else if strings.Contains(matches[0], "_alpha") {
ans.Version = matches[0][1:strings.Index(matches[0], "_alpha")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_alpha"):]
} else if strings.Contains(matches[0], "_beta") {
ans.Version = matches[0][1:strings.Index(matches[0], "_beta")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_beta"):]
} else if strings.Contains(matches[0], "-r") {
ans.Version = matches[0][1:strings.Index(matches[0], "-r")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "-r"):]
} else {
ans.Version = matches[0][1:]
}
ans.Name = pkgname[0 : len(pkgname)-len(ans.Version)-1-len(ans.VersionSuffix)]
} else {
ans.Name = pkgname
}
// Set condition if there isn't a prefix but only a version
if ans.Condition == PkgCondInvalid && ans.Version != "" {
ans.Condition = PkgCondEqual
}
return &ans, nil
}