diff --git a/info.go b/info.go index a1fbf7f..ebe56d5 100644 --- a/info.go +++ b/info.go @@ -62,7 +62,11 @@ func infoCmd(c *cli.Context) error { if err != nil { log.Fatal("Error parsing os-release file").Err(err).Send() } - names, err = overrides.Resolve(info, overrides.DefaultOpts) + names, err = overrides.Resolve( + info, + overrides.DefaultOpts. + WithLanguages([]string{overrides.SystemLang()}), + ) if err != nil { log.Fatal("Error resolving overrides").Err(err).Send() } @@ -70,33 +74,17 @@ func infoCmd(c *cli.Context) error { for _, pkg := range pkgs { if !all { - depsSet := false - buildDepsSet := false - for _, name := range names { - if deps, ok := pkg.Depends.Val[name]; ok && !depsSet { - pkg.Depends.Val = map[string][]string{name: deps} - depsSet = true - } - - if buildDeps, ok := pkg.BuildDepends.Val[name]; ok && !buildDepsSet { - pkg.BuildDepends.Val = map[string][]string{name: buildDeps} - buildDepsSet = true - } + err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) + if err != nil { + log.Fatal("Error encoding script variables").Err(err).Send() } - - if !depsSet { - pkg.Depends.Val = nil - } - - if !buildDepsSet { - pkg.BuildDepends.Val = nil + } else { + err = yaml.NewEncoder(os.Stdout).Encode(pkg) + if err != nil { + log.Fatal("Error encoding script variables").Err(err).Send() } } - err = yaml.NewEncoder(os.Stdout).Encode(pkg) - if err != nil { - log.Fatal("Error encoding script variables").Err(err).Send() - } fmt.Println("---") } diff --git a/internal/db/db.go b/internal/db/db.go index 991f6ff..5bea3c4 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -27,9 +27,9 @@ type Package struct { Version string `sh:"version,required" db:"version"` Release int `sh:"release,required" db:"release"` Epoch uint `sh:"epoch" db:"epoch"` - Description string `sh:"desc" db:"description"` - Homepage string `sh:"homepage" db:"homepage"` - Maintainer string `sh:"maintainer" db:"maintainer"` + Description JSON[map[string]string] `db:"description"` + Homepage JSON[map[string]string] `db:"homepage"` + Maintainer JSON[map[string]string] `db:"maintainer"` Architectures JSON[[]string] `sh:"architectures" db:"architectures"` Licenses JSON[[]string] `sh:"license" db:"licenses"` Provides JSON[[]string] `sh:"provides" db:"provides"` @@ -84,9 +84,9 @@ func Init(db *sqlx.DB, dsn string) error { version TEXT NOT NULL, release INT NOT NULL, epoch INT, - description TEXT, - homepage TEXT, - maintainer TEXT, + description TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')), + homepage TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')), + maintainer TEXT CHECK(maintainer = 'null' OR (JSON_VALID(maintainer) AND JSON_TYPE(maintainer) = 'object')), architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')), licenses TEXT CHECK(licenses = 'null' OR (JSON_VALID(licenses) AND JSON_TYPE(licenses) = 'array')), provides TEXT CHECK(provides = 'null' OR (JSON_VALID(provides) AND JSON_TYPE(provides) = 'array')), diff --git a/internal/db/db_test.go b/internal/db/db_test.go index 80ece7b..4bc2e57 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -10,13 +10,21 @@ import ( ) var testPkg = db.Package{ - Name: "test", - Version: "0.0.1", - Release: 1, - Epoch: 2, - Description: "Test package", - Homepage: "https://lure.arsenm.dev", - Maintainer: "Arsen Musayelyan ", + Name: "test", + Version: "0.0.1", + Release: 1, + Epoch: 2, + Description: db.NewJSON(map[string]string{ + "en": "Test package", + "ru": "Проверочный пакет", + }), + Homepage: db.NewJSON(map[string]string{ + "en": "https://lure.arsenm.dev", + }), + Maintainer: db.NewJSON(map[string]string{ + "en": "Arsen Musayelyan ", + "ru": "Арсен Мусаелян ", + }), Architectures: db.NewJSON([]string{"arm64", "amd64"}), Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}), Provides: db.NewJSON([]string{"test"}), diff --git a/internal/overrides/overrides.go b/internal/overrides/overrides.go index a7a156e..5387b28 100644 --- a/internal/overrides/overrides.go +++ b/internal/overrides/overrides.go @@ -2,20 +2,23 @@ package overrides import ( "os" + "reflect" "runtime" "strings" "go.arsenm.dev/lure/distro" "go.arsenm.dev/lure/internal/cpu" + "go.arsenm.dev/lure/internal/db" "golang.org/x/exp/slices" "golang.org/x/text/language" ) type Opts struct { - Name string - Overrides bool - LikeDistros bool - Languages []string + Name string + Overrides bool + LikeDistros bool + Languages []string + LanguageTags []language.Tag } var DefaultOpts = &Opts{ @@ -34,7 +37,7 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) { return []string{opts.Name}, nil } - langs, err := parseLangs(opts.Languages) + langs, err := parseLangs(opts.Languages, opts.LanguageTags) if err != nil { return nil, err } @@ -123,15 +126,91 @@ func (o *Opts) WithLikeDistros(v bool) *Opts { return out } -func parseLangs(langs []string) ([]string, error) { - out := make([]string, len(langs)) +func (o *Opts) WithLanguages(langs []string) *Opts { + out := &Opts{} + *out = *o + + out.Languages = langs + return out +} + +func (o *Opts) WithLanguageTags(langs []string) *Opts { + out := &Opts{} + *out = *o + + out.Languages = langs + return out +} + +// ResolvedPackage is a LURE package after its overrides +// have been resolved +type ResolvedPackage struct { + Name string `sh:"name"` + Version string `sh:"version"` + Release int `sh:"release"` + Epoch uint `sh:"epoch"` + Description string `db:"description"` + Homepage string `db:"homepage"` + Maintainer string `db:"maintainer"` + Architectures []string `sh:"architectures"` + Licenses []string `sh:"license"` + Provides []string `sh:"provides"` + Conflicts []string `sh:"conflicts"` + Replaces []string `sh:"replaces"` + Depends []string `sh:"deps"` + BuildDepends []string `sh:"build_deps"` +} + +func ResolvePackage(pkg *db.Package, overrides []string) *ResolvedPackage { + out := &ResolvedPackage{} + outVal := reflect.ValueOf(out).Elem() + pkgVal := reflect.ValueOf(pkg).Elem() + + for i := 0; i < outVal.NumField(); i++ { + fieldVal := outVal.Field(i) + fieldType := fieldVal.Type() + pkgFieldVal := pkgVal.FieldByName(outVal.Type().Field(i).Name) + pkgFieldType := pkgFieldVal.Type() + + if strings.HasPrefix(pkgFieldType.String(), "db.JSON") { + pkgFieldVal = pkgFieldVal.FieldByName("Val") + pkgFieldType = pkgFieldVal.Type() + } + + if pkgFieldType.AssignableTo(fieldType) { + fieldVal.Set(pkgFieldVal) + continue + } + + if pkgFieldVal.Kind() == reflect.Map && pkgFieldType.Elem().AssignableTo(fieldType) { + for _, override := range overrides { + overrideVal := pkgFieldVal.MapIndex(reflect.ValueOf(override)) + if !overrideVal.IsValid() { + continue + } + + fieldVal.Set(overrideVal) + break + } + } + } + + return out +} + +func parseLangs(langs []string, tags []language.Tag) ([]string, error) { + out := make([]string, len(tags)+len(langs)) + for i, tag := range tags { + base, _ := tag.Base() + out[i] = base.String() + } for i, lang := range langs { tag, err := language.Parse(lang) if err != nil { return nil, err } base, _ := tag.Base() - out[i] = base.String() + out[len(tags)+i] = base.String() } slices.Sort(out) out = slices.Compact(out) @@ -140,8 +219,9 @@ func parseLangs(langs []string) ([]string, error) { func SystemLang() string { lang := os.Getenv("LANG") + lang, _, _ = strings.Cut(lang, ".") if lang == "" { lang = "en" } return lang -} \ No newline at end of file +} diff --git a/internal/overrides/overrides_test.go b/internal/overrides/overrides_test.go index 3b28159..11d2d82 100644 --- a/internal/overrides/overrides_test.go +++ b/internal/overrides/overrides_test.go @@ -6,6 +6,7 @@ import ( "go.arsenm.dev/lure/distro" "go.arsenm.dev/lure/internal/overrides" + "golang.org/x/text/language" ) var info = &distro.OSRelease{ @@ -108,8 +109,9 @@ func TestResolveNoOverrides(t *testing.T) { func TestResolveLangs(t *testing.T) { names, err := overrides.Resolve(info, &overrides.Opts{ - Overrides: true, - Languages: []string{"ru_RU", "en", "en_US"}, + Overrides: true, + Languages: []string{"ru_RU", "en", "en_US"}, + LanguageTags: []language.Tag{language.BritishEnglish}, }) if err != nil { t.Fatalf("Expected no error, got %s", err) diff --git a/internal/repos/find_test.go b/internal/repos/find_test.go index 761e26f..3ad10f1 100644 --- a/internal/repos/find_test.go +++ b/internal/repos/find_test.go @@ -73,24 +73,30 @@ func TestFindPkgsEmpty(t *testing.T) { defer removeCacheDir(t) err = db.InsertPackage(gdb, db.Package{ - Name: "test1", - Repository: "default", - Version: "0.0.1", - Release: 1, - Description: "Test package 1", - Provides: db.NewJSON([]string{""}), + Name: "test1", + Repository: "default", + Version: "0.0.1", + Release: 1, + Description: db.NewJSON(map[string]string{ + "en": "Test package 1", + "ru": "Проверочный пакет 1", + }), + Provides: db.NewJSON([]string{""}), }) if err != nil { t.Fatalf("Expected no error, got %s", err) } err = db.InsertPackage(gdb, db.Package{ - Name: "test2", - Repository: "default", - Version: "0.0.1", - Release: 1, - Description: "Test package 2", - Provides: db.NewJSON([]string{"test"}), + Name: "test2", + Repository: "default", + Version: "0.0.1", + Release: 1, + Description: db.NewJSON(map[string]string{ + "en": "Test package 2", + "ru": "Проверочный пакет 2", + }), + Provides: db.NewJSON([]string{"test"}), }) if err != nil { t.Fatalf("Expected no error, got %s", err) diff --git a/internal/repos/pull.go b/internal/repos/pull.go index 3456319..a15da23 100644 --- a/internal/repos/pull.go +++ b/internal/repos/pull.go @@ -7,6 +7,7 @@ import ( "net/url" "os" "path/filepath" + "reflect" "strings" "github.com/go-git/go-billy/v5" @@ -338,6 +339,9 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb * } pkg := db.Package{ + Description: db.NewJSON(map[string]string{}), + Homepage: db.NewJSON(map[string]string{}), + Maintainer: db.NewJSON(map[string]string{}), Depends: db.NewJSON(map[string][]string{}), BuildDepends: db.NewJSON(map[string][]string{}), Repository: repo.Name, @@ -378,20 +382,34 @@ func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runn return d.DecodeVars(pkg) } +var overridable = map[string]string{ + "deps": "Depends", + "build_deps": "BuildDepends", + "desc": "Description", + "homepage": "Homepage", + "maintainer": "Maintainer", +} + func resolveOverrides(runner *interp.Runner, pkg *db.Package) { + pkgVal := reflect.ValueOf(pkg).Elem() for name, val := range runner.Vars { - if strings.HasPrefix(name, "deps") { - override := strings.TrimPrefix(name, "deps") - override = strings.TrimPrefix(override, "_") + for prefix, field := range overridable { + if strings.HasPrefix(name, prefix) { + override := strings.TrimPrefix(name, prefix) + override = strings.TrimPrefix(override, "_") - pkg.Depends.Val[override] = val.List - } else if strings.HasPrefix(name, "build_deps") { - override := strings.TrimPrefix(name, "build_deps") - override = strings.TrimPrefix(override, "_") + field := pkgVal.FieldByName(field) + varVal := field.FieldByName("Val") + varType := varVal.Type() - pkg.BuildDepends.Val[override] = val.List - } else { - continue + switch varType.Elem().String() { + case "[]string": + varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List)) + case "string": + varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str)) + } + break + } } } }