// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. // It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов. // // ALR - Any Linux Repository // Copyright (C) 2025 Евгений Храмов // // 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 . package config import ( "log/slog" "os" "path/filepath" "reflect" "github.com/caarlos0/env" "github.com/leonelquinteros/gotext" "github.com/pelletier/go-toml/v2" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" ) type ALRConfig struct { cfg *types.Config paths *Paths } var defaultConfig = &types.Config{ RootCmd: "sudo", PagerStyle: "native", IgnorePkgUpdates: []string{}, AutoPull: true, Repos: []types.Repo{}, } func New() *ALRConfig { return &ALRConfig{} } func readConfig(path string) (*types.Config, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() config := types.Config{} if err := toml.NewDecoder(file).Decode(&config); err != nil { return nil, err } return &config, nil } func mergeStructs(dst, src interface{}) { srcVal := reflect.ValueOf(src) if srcVal.IsNil() { return } srcVal = srcVal.Elem() dstVal := reflect.ValueOf(dst).Elem() for i := range srcVal.NumField() { srcField := srcVal.Field(i) srcFieldName := srcVal.Type().Field(i).Name dstField := dstVal.FieldByName(srcFieldName) if dstField.IsValid() && dstField.CanSet() { dstField.Set(srcField) } } } const systemConfigPath = "/etc/alr/alr.toml" func (c *ALRConfig) Load() error { systemConfig, err := readConfig( systemConfigPath, ) if err != nil { slog.Debug("Cannot read system config", "err", err) } cfgDir, err := os.UserConfigDir() if err != nil { slog.Debug("Cannot read user config directory") } userConfigPath := filepath.Join(cfgDir, "alr", "alr.toml") userConfig, err := readConfig( userConfigPath, ) if err != nil { slog.Debug("Cannot read user config") } config := &types.Config{} mergeStructs(config, defaultConfig) mergeStructs(config, systemConfig) mergeStructs(config, userConfig) err = env.Parse(config) if err != nil { return err } c.cfg = config cacheDir, err := os.UserCacheDir() if err != nil { return err } c.paths = &Paths{} c.paths.UserConfigPath = userConfigPath c.paths.CacheDir = filepath.Join(cacheDir, "alr") c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") c.initPaths() return nil } func (c *ALRConfig) RootCmd() string { return c.cfg.RootCmd } func (c *ALRConfig) PagerStyle() string { return c.cfg.PagerStyle } func (c *ALRConfig) AutoPull() bool { return c.cfg.AutoPull } func (c *ALRConfig) AllowRunAsRoot() bool { return c.cfg.Unsafe.AllowRunAsRoot } func (c *ALRConfig) Repos() []types.Repo { return c.cfg.Repos } func (c *ALRConfig) SetRepos(repos []types.Repo) { c.cfg.Repos = repos } func (c *ALRConfig) IgnorePkgUpdates() []string { return c.cfg.IgnorePkgUpdates } func (c *ALRConfig) LogLevel() string { return c.cfg.LogLevel } func (c *ALRConfig) GetPaths() *Paths { return c.paths } func (c *ALRConfig) initPaths() { err := os.MkdirAll(filepath.Dir(c.paths.UserConfigPath), 0o755) if err != nil { slog.Error(gotext.Get("Unable to create config directory"), "err", err) os.Exit(1) } err = os.MkdirAll(c.paths.RepoDir, 0o755) if err != nil { slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err) os.Exit(1) } err = os.MkdirAll(c.paths.PkgsDir, 0o755) if err != nil { slog.Error(gotext.Get("Unable to create package cache directory"), "err", err) os.Exit(1) } } func (c *ALRConfig) SaveUserConfig() error { f, err := os.Create(c.paths.UserConfigPath) if err != nil { return err } return toml.NewEncoder(f).Encode(c.cfg) }