From 25f544b8fe504708b13afd11a8261fad88d507f6 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 16 Dec 2023 06:19:53 -0800 Subject: [PATCH] refactor: zerolog -> slog, errors.Wrap -> fmt.Error (#49) * refactor: zerolog -> slog, errors.Wrap -> fmt.Error * chore: lint * fix: correctly handle errors * fix: use parsed level * fix: use parsed level, log json to file --- .golangci.yml | 2 - cli/cache/cache.go | 29 +++--- cli/cache/download.go | 26 ++--- cli/cache/integrity.go | 22 ++-- cli/context.go | 23 +++-- cli/disk/ftp.go | 64 +++++++----- cli/disk/main.go | 13 ++- cli/installations.go | 106 ++++++++++---------- cli/profiles.go | 30 +++--- cli/provider/ficsit.go | 2 +- cli/provider/local.go | 13 +-- cli/resolving_test.go | 6 +- cmd/cli.go | 12 ++- cmd/installation/set-profile.go | 3 +- cmd/installation/set-vanilla.go | 3 +- cmd/profile/mods.go | 3 +- cmd/root.go | 57 +++++++---- cmd/search.go | 3 +- cmd/smr/upload.go | 57 +++++------ ficsit/root.go | 8 +- ficsit/utils/json.go | 5 +- go.mod | 19 ++-- go.sum | 39 ++++--- tea/root.go | 7 +- tea/scenes/installation/new_installation.go | 2 +- tea/scenes/main_menu.go | 6 +- tea/scenes/mods/mod.go | 4 +- tea/scenes/mods/mod_info.go | 6 +- tea/scenes/mods/mod_semver.go | 2 +- tea/scenes/mods/mod_version.go | 4 +- tea/scenes/profile/new_profile.go | 4 +- tea/scenes/profile/rename_profile.go | 2 +- tea/tea_test.go | 2 +- utils/io.go | 31 +++--- utils/structures.go | 7 +- 35 files changed, 331 insertions(+), 291 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index e7f9fa2..a831514 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,8 +4,6 @@ linters-settings: - .Errorf( - errors.New( - errors.Unwrap( - - .Wrap( - - .Wrapf( - .WithMessage( - .WithMessagef( - .WithStack( diff --git a/cli/cache/cache.go b/cli/cache/cache.go index 89fdc35..9400662 100644 --- a/cli/cache/cache.go +++ b/cli/cache/cache.go @@ -4,14 +4,15 @@ import ( "archive/zip" "encoding/base64" "encoding/json" + "errors" + "fmt" "io" + "log/slog" "os" "path/filepath" "strings" - "github.com/pkg/errors" "github.com/puzpuzpuz/xsync/v3" - "github.com/rs/zerolog/log" "github.com/spf13/viper" ) @@ -52,7 +53,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) { items, err := os.ReadDir(downloadCache) if err != nil { - return nil, errors.Wrap(err, "failed reading download cache") + return nil, fmt.Errorf("failed reading download cache: %w", err) } for _, item := range items { @@ -65,7 +66,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) { _, err = addFileToCache(item.Name()) if err != nil { - log.Err(err).Str("file", item.Name()).Msg("failed to add file to cache") + slog.Error("failed to add file to cache", slog.String("file", item.Name()), slog.Any("err", err)) } } return loadedCache, nil @@ -74,7 +75,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) { func addFileToCache(filename string) (*File, error) { cacheFile, err := readCacheFile(filename) if err != nil { - return nil, errors.Wrap(err, "failed to read cache file") + return nil, fmt.Errorf("failed to read cache file: %w", err) } loadedCache.Compute(cacheFile.ModReference, func(oldValue []File, _ bool) ([]File, bool) { @@ -89,19 +90,19 @@ func readCacheFile(filename string) (*File, error) { path := filepath.Join(downloadCache, filename) stat, err := os.Stat(path) if err != nil { - return nil, errors.Wrap(err, "failed to stat file") + return nil, fmt.Errorf("failed to stat file: %w", err) } zipFile, err := os.Open(path) if err != nil { - return nil, errors.Wrap(err, "failed to open file") + return nil, fmt.Errorf("failed to open file: %w", err) } defer zipFile.Close() size := stat.Size() reader, err := zip.NewReader(zipFile, size) if err != nil { - return nil, errors.Wrap(err, "failed to read zip") + return nil, fmt.Errorf("failed to read zip: %w", err) } var upluginFile *zip.File @@ -117,23 +118,23 @@ func readCacheFile(filename string) (*File, error) { upluginReader, err := upluginFile.Open() if err != nil { - return nil, errors.Wrap(err, "failed to open uplugin file") + return nil, fmt.Errorf("failed to open uplugin file: %w", err) } var uplugin UPlugin data, err := io.ReadAll(upluginReader) if err != nil { - return nil, errors.Wrap(err, "failed to read uplugin file") + return nil, fmt.Errorf("failed to read uplugin file: %w", err) } if err := json.Unmarshal(data, &uplugin); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal uplugin file") + return nil, fmt.Errorf("failed to unmarshal uplugin file: %w", err) } modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin") hash, err := getFileHash(filename) if err != nil { - return nil, errors.Wrap(err, "failed to get file hash") + return nil, fmt.Errorf("failed to get file hash: %w", err) } var iconFile *zip.File @@ -147,13 +148,13 @@ func readCacheFile(filename string) (*File, error) { if iconFile != nil { iconReader, err := iconFile.Open() if err != nil { - return nil, errors.Wrap(err, "failed to open icon file") + return nil, fmt.Errorf("failed to open icon file: %w", err) } defer iconReader.Close() data, err := io.ReadAll(iconReader) if err != nil { - return nil, errors.Wrap(err, "failed to read icon file") + return nil, fmt.Errorf("failed to read icon file: %w", err) } iconData := base64.StdEncoding.EncodeToString(data) icon = &iconData diff --git a/cli/cache/download.go b/cli/cache/download.go index e56770e..33c7317 100644 --- a/cli/cache/download.go +++ b/cli/cache/download.go @@ -1,6 +1,7 @@ package cache import ( + "errors" "fmt" "io" "net/http" @@ -8,7 +9,6 @@ import ( "path/filepath" "sync" - "github.com/pkg/errors" "github.com/puzpuzpuz/xsync/v3" "github.com/spf13/viper" @@ -42,7 +42,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") if err := os.MkdirAll(downloadCache, 0o777); err != nil { if !os.IsExist(err) { - return nil, 0, errors.Wrap(err, "failed creating download cache") + return nil, 0, fmt.Errorf("failed creating download cache: %w", err) } } @@ -61,7 +61,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut f, err := os.Open(location) if err != nil { - return nil, 0, errors.Wrap(err, "failed to open file: "+location) + return nil, 0, fmt.Errorf("failed to open file: %s: %w", location, err) } return f, group.size, nil @@ -107,7 +107,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut f, err := os.Open(location) if err != nil { - return nil, 0, errors.Wrap(err, "failed to open file: "+location) + return nil, 0, fmt.Errorf("failed to open file: %s: %w", location, err) } return f, size, nil @@ -121,13 +121,13 @@ func downloadInternal(cacheKey string, location string, hash string, url string, if hash != "" { f, err := os.Open(location) if err != nil { - return 0, errors.Wrap(err, "failed to open file: "+location) + return 0, fmt.Errorf("failed to open file: %s: %w", location, err) } defer f.Close() existingHash, err = utils.SHA256Data(f) if err != nil { - return 0, errors.Wrap(err, "could not compute hash for file: "+location) + return 0, fmt.Errorf("could not compute hash for file: %s: %w", location, err) } } @@ -136,16 +136,16 @@ func downloadInternal(cacheKey string, location string, hash string, url string, } if err := os.Remove(location); err != nil { - return 0, errors.Wrap(err, "failed to delete file: "+location) + return 0, fmt.Errorf("failed to delete file: %s: %w", location, err) } } else if !os.IsNotExist(err) { - return 0, errors.Wrap(err, "failed to stat file: "+location) + return 0, fmt.Errorf("failed to stat file: %s: %w", location, err) } if updates != nil { headResp, err := http.Head(url) if err != nil { - return 0, errors.Wrap(err, "failed to head: "+url) + return 0, fmt.Errorf("failed to head: %s: %w", url, err) } defer headResp.Body.Close() updates <- utils.GenericProgress{Total: headResp.ContentLength} @@ -158,13 +158,13 @@ func downloadInternal(cacheKey string, location string, hash string, url string, out, err := os.Create(location) if err != nil { - return 0, errors.Wrap(err, "failed creating file at: "+location) + return 0, fmt.Errorf("failed creating file at: %s: %w", location, err) } defer out.Close() resp, err := http.Get(url) if err != nil { - return 0, errors.Wrap(err, "failed to fetch: "+url) + return 0, fmt.Errorf("failed to fetch: %s: %w", url, err) } defer resp.Body.Close() @@ -180,7 +180,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string, _, err = io.Copy(out, progresser) if err != nil { - return 0, errors.Wrap(err, "failed writing file to disk") + return 0, fmt.Errorf("failed writing file to disk: %w", err) } if updates != nil { @@ -189,7 +189,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string, _, err = addFileToCache(cacheKey) if err != nil { - return 0, errors.Wrap(err, "failed to add file to cache") + return 0, fmt.Errorf("failed to add file to cache: %w", err) } return resp.ContentLength, nil diff --git a/cli/cache/integrity.go b/cli/cache/integrity.go index 6e9ea64..f60d7bf 100644 --- a/cli/cache/integrity.go +++ b/cli/cache/integrity.go @@ -2,14 +2,14 @@ package cache import ( "encoding/json" + "fmt" "io" + "log/slog" "os" "path/filepath" "time" - "github.com/pkg/errors" "github.com/puzpuzpuz/xsync/v3" - "github.com/rs/zerolog/log" "github.com/spf13/viper" "github.com/satisfactorymodding/ficsit-cli/utils" @@ -36,7 +36,7 @@ func getFileHash(file string) (string, error) { downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") stat, err := os.Stat(filepath.Join(downloadCache, file)) if err != nil { - return "", errors.Wrap(err, "failed to stat file") + return "", fmt.Errorf("failed to stat file: %w", err) } if stat.Size() != cachedHash.Size || stat.ModTime() != cachedHash.Modified { return cacheFileHash(file) @@ -48,16 +48,16 @@ func cacheFileHash(file string) (string, error) { downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") stat, err := os.Stat(filepath.Join(downloadCache, file)) if err != nil { - return "", errors.Wrap(err, "failed to stat file") + return "", fmt.Errorf("failed to stat file: %w", err) } f, err := os.Open(filepath.Join(downloadCache, file)) if err != nil { - return "", errors.Wrap(err, "failed to open file") + return "", fmt.Errorf("failed to open file: %w", err) } defer f.Close() hash, err := utils.SHA256Data(f) if err != nil { - return "", errors.Wrap(err, "failed to hash file") + return "", fmt.Errorf("failed to hash file: %w", err) } hashCache.Store(file, hashInfo{ Hash: hash, @@ -76,19 +76,19 @@ func loadHashCache() { } f, err := os.Open(cacheFile) if err != nil { - log.Warn().Err(err).Msg("failed to open hash cache, recreating") + slog.Warn("failed to open hash cache, recreating", slog.Any("err", err)) return } defer f.Close() hashCacheJSON, err := io.ReadAll(f) if err != nil { - log.Warn().Err(err).Msg("failed to read hash cache, recreating") + slog.Warn("failed to read hash cache, recreating", slog.Any("err", err)) return } if err := json.Unmarshal(hashCacheJSON, &hashCache); err != nil { - log.Warn().Err(err).Msg("failed to unmarshal hash cache, recreating") + slog.Warn("failed to unmarshal hash cache, recreating", slog.Any("err", err)) return } } @@ -102,12 +102,12 @@ func saveHashCache() { }) hashCacheJSON, err := json.Marshal(plainCache) if err != nil { - log.Warn().Err(err).Msg("failed to marshal hash cache") + slog.Warn("failed to marshal hash cache", slog.Any("err", err)) return } if err := os.WriteFile(cacheFile, hashCacheJSON, 0o755); err != nil { - log.Warn().Err(err).Msg("failed to write hash cache") + slog.Warn("failed to write hash cache", slog.Any("err", err)) return } } diff --git a/cli/context.go b/cli/context.go index 66333ec..1e33f5f 100644 --- a/cli/context.go +++ b/cli/context.go @@ -1,8 +1,9 @@ package cli import ( + "fmt" + "github.com/Khan/genqlient/graphql" - "github.com/pkg/errors" "github.com/spf13/viper" "github.com/satisfactorymodding/ficsit-cli/cli/cache" @@ -35,17 +36,17 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) { if !apiOnly { profiles, err := InitProfiles() if err != nil { - return nil, errors.Wrap(err, "failed to initialize profiles") + return nil, fmt.Errorf("failed to initialize profiles: %w", err) } installations, err := InitInstallations() if err != nil { - return nil, errors.Wrap(err, "failed to initialize installations") + return nil, fmt.Errorf("failed to initialize installations: %w", err) } _, err = cache.LoadCache() if err != nil { - return nil, errors.Wrap(err, "failed to load cache") + return nil, fmt.Errorf("failed to load cache: %w", err) } globalContext = &GlobalContext{ @@ -70,12 +71,12 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) { func (g *GlobalContext) ReInit() error { profiles, err := InitProfiles() if err != nil { - return errors.Wrap(err, "failed to initialize profiles") + return fmt.Errorf("failed to initialize profiles: %w", err) } installations, err := InitInstallations() if err != nil { - return errors.Wrap(err, "failed to initialize installations") + return fmt.Errorf("failed to initialize installations: %w", err) } g.Installations = installations @@ -89,18 +90,18 @@ func (g *GlobalContext) Wipe() error { // Wipe all installations for _, installation := range g.Installations.Installations { if err := installation.Wipe(); err != nil { - return errors.Wrap(err, "failed wiping installation") + return fmt.Errorf("failed wiping installation: %w", err) } if err := g.Installations.DeleteInstallation(installation.Path); err != nil { - return errors.Wrap(err, "failed deleting installation") + return fmt.Errorf("failed deleting installation: %w", err) } } // Wipe all profiles for _, profile := range g.Profiles.Profiles { if err := g.Profiles.DeleteProfile(profile.Name); err != nil { - return errors.Wrap(err, "failed deleting profile") + return fmt.Errorf("failed deleting profile: %w", err) } } @@ -109,11 +110,11 @@ func (g *GlobalContext) Wipe() error { func (g *GlobalContext) Save() error { if err := g.Installations.Save(); err != nil { - return errors.Wrap(err, "failed to save installations") + return fmt.Errorf("failed to save installations: %w", err) } if err := g.Profiles.Save(); err != nil { - return errors.Wrap(err, "failed to save profiles") + return fmt.Errorf("failed to save profiles: %w", err) } return nil diff --git a/cli/disk/ftp.go b/cli/disk/ftp.go index a0dd1be..c72524d 100644 --- a/cli/disk/ftp.go +++ b/cli/disk/ftp.go @@ -2,15 +2,15 @@ package disk import ( "bytes" + "fmt" "io" + "log/slog" "net/url" "strings" "sync" "time" "github.com/jlaffaye/ftp" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" ) var _ Disk = (*ftpDisk)(nil) @@ -36,20 +36,20 @@ func (f ftpEntry) Name() string { func newFTP(path string) (Disk, error) { u, err := url.Parse(path) if err != nil { - return nil, errors.Wrap(err, "failed to parse ftp url") + return nil, fmt.Errorf("failed to parse ftp url: %w", err) } c, err := ftp.Dial(u.Host, ftp.DialWithTimeout(time.Second*5)) if err != nil { - return nil, errors.Wrap(err, "failed to dial host "+u.Host) + return nil, fmt.Errorf("failed to dial host %s: %w", u.Host, err) } password, _ := u.User.Password() if err := c.Login(u.User.Username(), password); err != nil { - return nil, errors.Wrap(err, "failed to login") + return nil, fmt.Errorf("failed to login: %w", err) } - log.Debug().Msg("logged into ftp") + slog.Debug("logged into ftp") return &ftpDisk{ path: u.Path, @@ -61,52 +61,64 @@ func (l *ftpDisk) Exists(path string) error { l.stepLock.Lock() defer l.stepLock.Unlock() - log.Debug().Str("path", path).Str("schema", "ftp").Msg("checking if file exists") + slog.Debug("checking if file exists", slog.String("path", path), slog.String("schema", "ftp")) _, err := l.client.FileSize(path) - return errors.Wrap(err, "failed to check if file exists") + return fmt.Errorf("failed to check if file exists: %w", err) } func (l *ftpDisk) Read(path string) ([]byte, error) { l.stepLock.Lock() defer l.stepLock.Unlock() - log.Debug().Str("path", path).Str("schema", "ftp").Msg("reading file") + slog.Debug("reading file", slog.String("path", path), slog.String("schema", "ftp")) f, err := l.client.Retr(path) if err != nil { - return nil, errors.Wrap(err, "failed to retrieve path") + return nil, fmt.Errorf("failed to retrieve path: %w", err) } defer f.Close() data, err := io.ReadAll(f) - return data, errors.Wrap(err, "failed to read file") + if err != nil { + return nil, fmt.Errorf("failed to read file: %w", err) + } + + return data, nil } func (l *ftpDisk) Write(path string, data []byte) error { l.stepLock.Lock() defer l.stepLock.Unlock() - log.Debug().Str("path", path).Str("schema", "ftp").Msg("writing to file") - return errors.Wrap(l.client.Stor(path, bytes.NewReader(data)), "failed to write file") + slog.Debug("writing to file", slog.String("path", path), slog.String("schema", "ftp")) + if err := l.client.Stor(path, bytes.NewReader(data)); err != nil { + return fmt.Errorf("failed to write file: %w", err) + } + + return nil } func (l *ftpDisk) Remove(path string) error { l.stepLock.Lock() defer l.stepLock.Unlock() - log.Debug().Str("path", path).Str("schema", "ftp").Msg("deleting path") - return errors.Wrap(l.client.Delete(path), "failed to delete path") + slog.Debug("deleting path", slog.String("path", path), slog.String("schema", "ftp")) + if err := l.client.Delete(path); err != nil { + return fmt.Errorf("failed to delete path: %w", err) + } + + return nil } func (l *ftpDisk) MkDir(path string) error { l.stepLock.Lock() defer l.stepLock.Unlock() - log.Debug().Str("schema", "ftp").Msg("going to root directory") + slog.Debug("going to root directory", slog.String("schema", "ftp")) err := l.client.ChangeDir("/") if err != nil { - return errors.Wrap(err, "failed to change directory") + return fmt.Errorf("failed to change directory: %w", err) } split := strings.Split(path[1:], "/") @@ -125,15 +137,15 @@ func (l *ftpDisk) MkDir(path string) error { } if !foundDir { - log.Debug().Str("dir", s).Str("schema", "ftp").Msg("making directory") + slog.Debug("making directory", slog.String("dir", s), slog.String("schema", "ftp")) if err := l.client.MakeDir(s); err != nil { - return errors.Wrap(err, "failed to make directory") + return fmt.Errorf("failed to make directory: %w", err) } } - log.Debug().Str("dir", s).Str("schema", "ftp").Msg("entering directory") + slog.Debug("entering directory", slog.String("dir", s), slog.String("schema", "ftp")) if err := l.client.ChangeDir(s); err != nil { - return errors.Wrap(err, "failed to enter directory") + return fmt.Errorf("failed to enter directory: %w", err) } } @@ -150,11 +162,11 @@ func (l *ftpDisk) ReadDirLock(path string, lock bool) ([]Entry, error) { defer l.stepLock.Unlock() } - log.Debug().Str("path", path).Str("schema", "ftp").Msg("reading directory") + slog.Debug("reading directory", slog.String("path", path), slog.String("schema", "ftp")) dir, err := l.client.List(path) if err != nil { - return nil, errors.Wrap(err, "failed to list files in directory") + return nil, fmt.Errorf("failed to list files in directory: %w", err) } entries := make([]Entry, len(dir)) @@ -178,7 +190,7 @@ func (l *ftpDisk) IsExist(err error) bool { func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) { reader, writer := io.Pipe() - log.Debug().Str("path", path).Str("schema", "ftp").Msg("opening for writing") + slog.Debug("opening for writing", slog.String("path", path), slog.String("schema", "ftp")) go func() { l.stepLock.Lock() @@ -186,9 +198,9 @@ func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) { err := l.client.Stor(path, reader) if err != nil { - log.Err(err).Msg("failed to store file") + slog.Error("failed to store file", slog.Any("err", err)) } - log.Debug().Str("path", path).Str("schema", "ftp").Msg("write success") + slog.Debug("write success", slog.String("path", path), slog.String("schema", "ftp")) }() return writer, nil diff --git a/cli/disk/main.go b/cli/disk/main.go index 018a2ae..cf04510 100644 --- a/cli/disk/main.go +++ b/cli/disk/main.go @@ -1,11 +1,10 @@ package disk import ( + "fmt" "io" + "log/slog" "net/url" - - "github.com/pkg/errors" - "github.com/rs/zerolog/log" ) type Disk interface { @@ -49,18 +48,18 @@ type Entry interface { func FromPath(path string) (Disk, error) { parsed, err := url.Parse(path) if err != nil { - return nil, errors.Wrap(err, "failed to parse path") + return nil, fmt.Errorf("failed to parse path: %w", err) } switch parsed.Scheme { case "ftp": - log.Info().Str("path", path).Msg("connecting to ftp") + slog.Info("connecting to ftp", slog.String("path", path)) return newFTP(path) case "sftp": - log.Info().Str("path", path).Msg("connecting to sftp") + slog.Info("connecting to sftp", slog.String("path", path)) return newSFTP(path) } - log.Info().Str("path", path).Msg("using local disk") + slog.Info("using local disk", slog.String("path", path)) return newLocal(path) } diff --git a/cli/installations.go b/cli/installations.go index fbc853f..f3d5e81 100644 --- a/cli/installations.go +++ b/cli/installations.go @@ -2,7 +2,9 @@ package cli import ( "encoding/json" + "errors" "fmt" + "log/slog" "net/url" "os" "path/filepath" @@ -10,8 +12,6 @@ import ( "strings" "sync" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/spf13/viper" "golang.org/x/sync/errgroup" @@ -50,18 +50,18 @@ func InitInstallations() (*Installations, error) { _, err := os.Stat(installationsFile) if err != nil { if !os.IsNotExist(err) { - return nil, errors.Wrap(err, "failed to stat installations file") + return nil, fmt.Errorf("failed to stat installations file: %w", err) } _, err := os.Stat(localDir) if err != nil { if !os.IsNotExist(err) { - return nil, errors.Wrap(err, "failed to read cache directory") + return nil, fmt.Errorf("failed to read cache directory: %w", err) } err = os.MkdirAll(localDir, 0o755) if err != nil { - return nil, errors.Wrap(err, "failed to create cache directory") + return nil, fmt.Errorf("failed to create cache directory: %w", err) } } @@ -70,18 +70,18 @@ func InitInstallations() (*Installations, error) { } if err := emptyInstallations.Save(); err != nil { - return nil, errors.Wrap(err, "failed to save empty installations") + return nil, fmt.Errorf("failed to save empty installations: %w", err) } } installationsData, err := os.ReadFile(installationsFile) if err != nil { - return nil, errors.Wrap(err, "failed to read installations") + return nil, fmt.Errorf("failed to read installations: %w", err) } var installations Installations if err := json.Unmarshal(installationsData, &installations); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal installations") + return nil, fmt.Errorf("failed to unmarshal installations: %w", err) } if installations.Version >= nextInstallationsVersion { @@ -93,21 +93,21 @@ func InitInstallations() (*Installations, error) { func (i *Installations) Save() error { if viper.GetBool("dry-run") { - log.Info().Msg("dry-run: skipping installation saving") + slog.Info("dry-run: skipping installation saving") return nil } installationsFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("installations-file")) - log.Info().Str("path", installationsFile).Msg("saving installations") + slog.Info("saving installations", slog.String("path", installationsFile)) installationsJSON, err := json.MarshalIndent(i, "", " ") if err != nil { - return errors.Wrap(err, "failed to marshal installations") + return fmt.Errorf("failed to marshal installations: %w", err) } if err := os.WriteFile(installationsFile, installationsJSON, 0o755); err != nil { - return errors.Wrap(err, "failed to write installations") + return fmt.Errorf("failed to write installations: %w", err) } return nil @@ -116,7 +116,7 @@ func (i *Installations) Save() error { func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, profile string) (*Installation, error) { parsed, err := url.Parse(installPath) if err != nil { - return nil, errors.Wrap(err, "failed to parse path") + return nil, fmt.Errorf("failed to parse path: %w", err) } absolutePath := installPath @@ -124,7 +124,7 @@ func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, absolutePath, err = filepath.Abs(installPath) if err != nil { - return nil, errors.Wrap(err, "could not resolve absolute path of: "+installPath) + return nil, fmt.Errorf("could not resolve absolute path of: %s: %w", installPath, err) } } @@ -135,7 +135,7 @@ func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, } if err := installation.Validate(ctx); err != nil { - return nil, errors.Wrap(err, "failed to validate installation") + return nil, fmt.Errorf("failed to validate installation: %w", err) } found := false @@ -206,7 +206,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error { err = d.Exists(filepath.Join(i.BasePath(), "FactoryGame.exe")) if err != nil { if !d.IsNotExist(err) { - return errors.Wrap(err, "failed reading FactoryGame.exe") + return fmt.Errorf("failed reading FactoryGame.exe: %w", err) } } else { foundExecutable = true @@ -215,7 +215,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error { err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.sh")) if err != nil { if !d.IsNotExist(err) { - return errors.Wrap(err, "failed reading FactoryServer.sh") + return fmt.Errorf("failed reading FactoryServer.sh: %w", err) } } else { foundExecutable = true @@ -224,7 +224,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error { err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.exe")) if err != nil { if !d.IsNotExist(err) { - return errors.Wrap(err, "failed reading FactoryServer.exe") + return fmt.Errorf("failed reading FactoryServer.exe: %w", err) } } else { foundExecutable = true @@ -273,11 +273,11 @@ func (i *Installation) LockFile(ctx *GlobalContext) (*resolver.LockFile, error) lockFileJSON, err := d.Read(lockfilePath) if err != nil { if !d.IsNotExist(err) { - return nil, errors.Wrap(err, "failed reading lockfile") + return nil, fmt.Errorf("failed reading lockfile: %w", err) } } else { if err := json.Unmarshal(lockFileJSON, &lockFile); err != nil { - return nil, errors.Wrap(err, "failed parsing lockfile") + return nil, fmt.Errorf("failed parsing lockfile: %w", err) } } @@ -298,17 +298,17 @@ func (i *Installation) WriteLockFile(ctx *GlobalContext, lockfile *resolver.Lock lockfileDir := filepath.Dir(lockfilePath) if err := d.Exists(lockfileDir); d.IsNotExist(err) { if err := d.MkDir(lockfileDir); err != nil { - return errors.Wrap(err, "failed creating lockfile directory") + return fmt.Errorf("failed creating lockfile directory: %w", err) } } marshaledLockfile, err := json.MarshalIndent(lockfile, "", " ") if err != nil { - return errors.Wrap(err, "failed to serialize lockfile json") + return fmt.Errorf("failed to serialize lockfile json: %w", err) } if err := d.Write(lockfilePath, marshaledLockfile); err != nil { - return errors.Wrap(err, "failed writing lockfile") + return fmt.Errorf("failed writing lockfile: %w", err) } return nil @@ -322,7 +322,7 @@ func (i *Installation) Wipe() error { modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods") if err := d.Remove(modsDirectory); err != nil { - return errors.Wrap(err, "failed removing Mods directory") + return fmt.Errorf("failed removing Mods directory: %w", err) } return nil @@ -338,16 +338,16 @@ func (i *Installation) ResolveProfile(ctx *GlobalContext) (*resolver.LockFile, e gameVersion, err := i.GetGameVersion(ctx) if err != nil { - return nil, errors.Wrap(err, "failed to detect game version") + return nil, fmt.Errorf("failed to detect game version: %w", err) } lockfile, err := ctx.Profiles.Profiles[i.Profile].Resolve(depResolver, lockFile, gameVersion) if err != nil { - return nil, errors.Wrap(err, "could not resolve mods") + return nil, fmt.Errorf("could not resolve mods: %w", err) } if err := i.WriteLockFile(ctx, lockfile); err != nil { - return nil, errors.Wrap(err, "failed to write lockfile") + return nil, fmt.Errorf("failed to write lockfile: %w", err) } return lockfile, nil @@ -375,12 +375,12 @@ type InstallUpdateItem struct { func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) error { if err := i.Validate(ctx); err != nil { - return errors.Wrap(err, "failed to validate installation") + return fmt.Errorf("failed to validate installation: %w", err) } platform, err := i.GetPlatform(ctx) if err != nil { - return errors.Wrap(err, "failed to detect platform") + return fmt.Errorf("failed to detect platform: %w", err) } lockfile := resolver.NewLockfile() @@ -389,7 +389,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) var err error lockfile, err = i.ResolveProfile(ctx) if err != nil { - return errors.Wrap(err, "failed to resolve lockfile") + return fmt.Errorf("failed to resolve lockfile: %w", err) } } @@ -400,12 +400,12 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods") if err := d.MkDir(modsDirectory); err != nil { - return errors.Wrap(err, "failed creating Mods directory") + return fmt.Errorf("failed creating Mods directory: %w", err) } dir, err := d.ReadDir(modsDirectory) if err != nil { - return errors.Wrap(err, "failed to read mods directory") + return fmt.Errorf("failed to read mods directory: %w", err) } for _, entry := range dir { @@ -414,16 +414,16 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) modDir := filepath.Join(modsDirectory, entry.Name()) err := d.Exists(filepath.Join(modDir, ".smm")) if err == nil { - log.Info().Str("mod_reference", entry.Name()).Msg("deleting mod") + slog.Info("deleting mod", slog.String("mod_reference", entry.Name())) if err := d.Remove(modDir); err != nil { - return errors.Wrap(err, "failed to delete mod directory") + return fmt.Errorf("failed to delete mod directory: %w", err) } } } } } - log.Info().Int("concurrency", viper.GetInt("concurrent-downloads")).Str("path", i.Path).Msg("starting installation") + slog.Info("starting installation", slog.Int("concurrency", viper.GetInt("concurrent-downloads")), slog.String("path", i.Path)) errg := errgroup.Group{} channelUsers := sync.WaitGroup{} @@ -461,14 +461,14 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) target, ok := version.Targets[platform.TargetName] if !ok { - return errors.Errorf("%s@%s not available for %s", modReference, version.Version, platform.TargetName) + return fmt.Errorf("%s@%s not available for %s", modReference, version.Version, platform.TargetName) } // Only install if a link is provided, otherwise assume mod is already installed if target.Link != "" { err := downloadAndExtractMod(modReference, version.Version, target.Link, target.Hash, modsDirectory, updates, downloadSemaphore, d) if err != nil { - return errors.Wrapf(err, "failed to install %s@%s", modReference, version.Version) + return fmt.Errorf("failed to install %s@%s: %w", modReference, version.Version, err) } } @@ -487,7 +487,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) } if err := errg.Wait(); err != nil { - return errors.Wrap(err, "failed to install mods") + return fmt.Errorf("failed to install mods: %w", err) } return nil @@ -495,19 +495,19 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error { if err := i.Validate(ctx); err != nil { - return errors.Wrap(err, "failed to validate installation") + return fmt.Errorf("failed to validate installation: %w", err) } lockFile, err := i.LockFile(ctx) if err != nil { - return errors.Wrap(err, "failed to read lock file") + return fmt.Errorf("failed to read lock file: %w", err) } resolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base")) gameVersion, err := i.GetGameVersion(ctx) if err != nil { - return errors.Wrap(err, "failed to detect game version") + return fmt.Errorf("failed to detect game version: %w", err) } profile := ctx.Profiles.GetProfile(i.Profile) @@ -521,11 +521,11 @@ func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error { newLockFile, err := profile.Resolve(resolver, lockFile, gameVersion) if err != nil { - return errors.Wrap(err, "failed to resolve dependencies") + return fmt.Errorf("failed to resolve dependencies: %w", err) } if err := i.WriteLockFile(ctx, newLockFile); err != nil { - return errors.Wrap(err, "failed to write lock file") + return fmt.Errorf("failed to write lock file: %w", err) } return nil @@ -552,10 +552,10 @@ func downloadAndExtractMod(modReference string, version string, link string, has }() } - log.Info().Str("mod_reference", modReference).Str("version", version).Str("link", link).Msg("downloading mod") + slog.Info("downloading mod", slog.String("mod_reference", modReference), slog.String("version", version), slog.String("link", link)) reader, size, err := cache.DownloadOrCache(modReference+"_"+version+".zip", hash, link, downloadUpdates, downloadSemaphore) if err != nil { - return errors.Wrap(err, "failed to download "+modReference+" from: "+link) + return fmt.Errorf("failed to download %s from: %s: %w", modReference, link, err) } defer reader.Close() @@ -583,9 +583,9 @@ func downloadAndExtractMod(modReference string, version string, link string, has }() } - log.Info().Str("mod_reference", modReference).Str("version", version).Str("link", link).Msg("extracting mod") + slog.Info("extracting mod", slog.String("mod_reference", modReference), slog.String("version", version), slog.String("link", link)) if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), hash, extractUpdates, d); err != nil { - return errors.Wrap(err, "could not extract "+modReference) + return fmt.Errorf("could not extract %s: %w", modReference, err) } if updates != nil { @@ -638,7 +638,7 @@ type gameVersionFile struct { func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) { if err := i.Validate(ctx); err != nil { - return 0, errors.Wrap(err, "failed to validate installation") + return 0, fmt.Errorf("failed to validate installation: %w", err) } platform, err := i.GetPlatform(ctx) @@ -655,14 +655,14 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) { file, err := d.Read(fullPath) if err != nil { if d.IsNotExist(err) { - return 0, errors.Wrap(err, "could not find game version file") + return 0, fmt.Errorf("could not find game version file: %w", err) } - return 0, errors.Wrap(err, "failed reading version file") + return 0, fmt.Errorf("failed reading version file: %w", err) } var versionData gameVersionFile if err := json.Unmarshal(file, &versionData); err != nil { - return 0, errors.Wrap(err, "failed to parse version file json") + return 0, fmt.Errorf("failed to parse version file json: %w", err) } return versionData.Changelist, nil @@ -670,7 +670,7 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) { func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) { if err := i.Validate(ctx); err != nil { - return nil, errors.Wrap(err, "failed to validate installation") + return nil, fmt.Errorf("failed to validate installation: %w", err) } d, err := i.GetDisk() @@ -685,7 +685,7 @@ func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) { if d.IsNotExist(err) { continue } - return nil, errors.Wrap(err, "failed detecting version file") + return nil, fmt.Errorf("failed detecting version file: %w", err) } return &platform, nil } diff --git a/cli/profiles.go b/cli/profiles.go index 9af83e9..46c4060 100644 --- a/cli/profiles.go +++ b/cli/profiles.go @@ -3,13 +3,13 @@ package cli import ( "context" "encoding/json" + "errors" "fmt" + "log/slog" "os" "path/filepath" "strings" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/spf13/viper" @@ -62,18 +62,18 @@ func InitProfiles() (*Profiles, error) { _, err := os.Stat(profilesFile) if err != nil { if !os.IsNotExist(err) { - return nil, errors.Wrap(err, "failed to stat profiles file") + return nil, fmt.Errorf("failed to stat profiles file: %w", err) } _, err := os.Stat(localDir) if err != nil { if !os.IsNotExist(err) { - return nil, errors.Wrap(err, "failed to read cache directory") + return nil, fmt.Errorf("failed to read cache directory: %w", err) } err = os.MkdirAll(localDir, 0o755) if err != nil { - return nil, errors.Wrap(err, "failed to create cache directory") + return nil, fmt.Errorf("failed to create cache directory: %w", err) } } @@ -94,13 +94,13 @@ func InitProfiles() (*Profiles, error) { if err == nil { manifestBytes, err := os.ReadFile(manifestFile) if err != nil { - log.Err(err).Str("file", manifestFile).Msg("Failed to read file, not importing profile") + slog.Error("Failed to read file, not importing profile", slog.Any("err", err), slog.String("file", manifestFile)) continue } var smmProfile smmProfileFile if err := json.Unmarshal(manifestBytes, &smmProfile); err != nil { - log.Err(err).Str("file", manifestFile).Msg("Failed to parse file, not importing profile") + slog.Error("Failed to parse file, not importing profile", slog.Any("err", err), slog.String("file", manifestFile)) continue } @@ -134,18 +134,18 @@ func InitProfiles() (*Profiles, error) { } if err := bootstrapProfiles.Save(); err != nil { - return nil, errors.Wrap(err, "failed to save empty profiles") + return nil, fmt.Errorf("failed to save empty profiles: %w", err) } } profilesData, err := os.ReadFile(profilesFile) if err != nil { - return nil, errors.Wrap(err, "failed to read profiles") + return nil, fmt.Errorf("failed to read profiles: %w", err) } var profiles Profiles if err := json.Unmarshal(profilesData, &profiles); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal profiles") + return nil, fmt.Errorf("failed to unmarshal profiles: %w", err) } if profiles.Version >= nextProfilesVersion { @@ -169,21 +169,21 @@ func InitProfiles() (*Profiles, error) { // Save the profiles to the profiles file. func (p *Profiles) Save() error { if viper.GetBool("dry-run") { - log.Info().Msg("dry-run: skipping profile saving") + slog.Info("dry-run: skipping profile saving") return nil } profilesFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("profiles-file")) - log.Info().Str("path", profilesFile).Msg("saving profiles") + slog.Info("saving profiles", slog.String("path", profilesFile)) profilesJSON, err := json.MarshalIndent(p, "", " ") if err != nil { - return errors.Wrap(err, "failed to marshal profiles") + return fmt.Errorf("failed to marshal profiles: %w", err) } if err := os.WriteFile(profilesFile, profilesJSON, 0o755); err != nil { - return errors.Wrap(err, "failed to write profiles") + return fmt.Errorf("failed to write profiles: %w", err) } return nil @@ -301,7 +301,7 @@ func (p *Profile) Resolve(resolver resolver.DependencyResolver, lockFile *resolv resultLockfile, err := resolver.ResolveModDependencies(context.TODO(), toResolve, lockFile, gameVersion, p.RequiredTargets) if err != nil { - return nil, errors.Wrap(err, "failed resolving profile dependencies") + return nil, fmt.Errorf("failed resolving profile dependencies: %w", err) } return resultLockfile, nil diff --git a/cli/provider/ficsit.go b/cli/provider/ficsit.go index fa60573..589b263 100644 --- a/cli/provider/ficsit.go +++ b/cli/provider/ficsit.go @@ -2,9 +2,9 @@ package provider import ( "context" + "errors" "github.com/Khan/genqlient/graphql" - "github.com/pkg/errors" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/satisfactorymodding/ficsit-cli/ficsit" diff --git a/cli/provider/local.go b/cli/provider/local.go index f59d9a0..d611f0e 100644 --- a/cli/provider/local.go +++ b/cli/provider/local.go @@ -2,10 +2,11 @@ package provider import ( "context" + "errors" + "fmt" "strings" "time" - "github.com/pkg/errors" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/satisfactorymodding/ficsit-cli/cli/cache" @@ -21,7 +22,7 @@ func NewLocalProvider() LocalProvider { func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit.ModsResponse, error) { cachedMods, err := cache.GetCache() if err != nil { - return nil, errors.Wrap(err, "failed to get cache") + return nil, fmt.Errorf("failed to get cache: %w", err) } mods := make([]ficsit.ModsModsGetModsModsMod, 0) @@ -93,7 +94,7 @@ func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.GetModResponse, error) { cachedModFiles, err := cache.GetCacheMod(modReference) if err != nil { - return nil, errors.Wrap(err, "failed to get cache") + return nil, fmt.Errorf("failed to get cache: %w", err) } if len(cachedModFiles) == 0 { @@ -129,7 +130,7 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, error) { cachedSMLFiles, err := cache.GetCacheMod("SML") if err != nil { - return nil, errors.Wrap(err, "failed to get cache") + return nil, fmt.Errorf("failed to get cache: %w", err) } smlVersions := make([]resolver.SMLVersion, 0) @@ -148,7 +149,7 @@ func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, er func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) { cachedModFiles, err := cache.GetCacheMod(modID) if err != nil { - return nil, errors.Wrap(err, "failed to get cache") + return nil, fmt.Errorf("failed to get cache: %w", err) } versions := make([]resolver.ModVersion, 0) @@ -166,7 +167,7 @@ func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID stri func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) { cachedModFiles, err := cache.GetCacheMod(modReference) if err != nil { - return nil, errors.Wrap(err, "failed to get cache") + return nil, fmt.Errorf("failed to get cache: %w", err) } if len(cachedModFiles) == 0 { diff --git a/cli/resolving_test.go b/cli/resolving_test.go index 811e094..12c159f 100644 --- a/cli/resolving_test.go +++ b/cli/resolving_test.go @@ -2,12 +2,12 @@ package cli import ( "context" + "log/slog" "math" "os" "testing" "github.com/MarvinJWendt/testza" - "github.com/rs/zerolog/log" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/spf13/viper" @@ -24,9 +24,9 @@ func installWatcher() chan<- InstallUpdate { for i := range c { if i.Progress.Total == i.Progress.Completed { if i.Type != InstallUpdateTypeOverall { - log.Info().Str("mod_reference", i.Item.Mod).Str("version", i.Item.Version).Str("type", string(i.Type)).Msg("progress completed") + slog.Info("progress completed", slog.String("mod_reference", i.Item.Mod), slog.String("version", i.Item.Version), slog.Any("type", i.Type)) } else { - log.Info().Msg("overall completed") + slog.Info("overall completed") } } } diff --git a/cmd/cli.go b/cmd/cli.go index 8157258..e0871a5 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -1,7 +1,8 @@ package cmd import ( - "github.com/rs/zerolog/log" + "log/slog" + "github.com/spf13/cobra" "github.com/spf13/viper" @@ -13,10 +14,11 @@ var cliCmd = &cobra.Command{ Use: "cli", Short: "Start interactive CLI (default)", RunE: func(cmd *cobra.Command, args []string) error { - log.Info(). - Str("version", viper.GetString("version")). - Str("commit", viper.GetString("commit")). - Msg("interactive cli initialized") + slog.Info( + "interactive cli initialized", + slog.String("version", viper.GetString("version")), + slog.String("commit", viper.GetString("commit")), + ) global, err := cli.InitCLI(false) if err != nil { diff --git a/cmd/installation/set-profile.go b/cmd/installation/set-profile.go index 44d35c0..401bb62 100644 --- a/cmd/installation/set-profile.go +++ b/cmd/installation/set-profile.go @@ -1,7 +1,8 @@ package installation import ( - "github.com/pkg/errors" + "errors" + "github.com/spf13/cobra" "github.com/satisfactorymodding/ficsit-cli/cli" diff --git a/cmd/installation/set-vanilla.go b/cmd/installation/set-vanilla.go index ededcac..08ceb7a 100644 --- a/cmd/installation/set-vanilla.go +++ b/cmd/installation/set-vanilla.go @@ -1,7 +1,8 @@ package installation import ( - "github.com/pkg/errors" + "errors" + "github.com/spf13/cobra" "github.com/spf13/viper" diff --git a/cmd/profile/mods.go b/cmd/profile/mods.go index addc48e..96a3ec1 100644 --- a/cmd/profile/mods.go +++ b/cmd/profile/mods.go @@ -1,7 +1,8 @@ package profile import ( - "github.com/pkg/errors" + "errors" + "github.com/spf13/cobra" "github.com/satisfactorymodding/ficsit-cli/cli" diff --git a/cmd/root.go b/cmd/root.go index 9a1b52f..f3b37a7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,16 +1,16 @@ package cmd import ( - "io" + "fmt" + "log/slog" "os" "path/filepath" "runtime" "time" - "github.com/pkg/errors" + "github.com/lmittmann/tint" "github.com/pterm/pterm" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" + slogmulti "github.com/samber/slog-multi" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -31,37 +31,56 @@ var RootCmd = &cobra.Command{ _ = viper.ReadInConfig() - level, err := zerolog.ParseLevel(viper.GetString("log")) - if err != nil { - panic(err) - } - - zerolog.SetGlobalLevel(level) - - writers := make([]io.Writer, 0) + handlers := make([]slog.Handler, 0) if viper.GetBool("pretty") { pterm.EnableStyling() } else { pterm.DisableStyling() } + const ( + ansiReset = "\033[0m" + ansiBold = "\033[1m" + ansiWhite = "\033[38m" + ansiBrightMagenta = "\033[95m" + ) + + level := slog.LevelInfo + if err := (&level).UnmarshalText([]byte(viper.GetString("log"))); err != nil { + return fmt.Errorf("failed parsing level: %w", err) + } + if !viper.GetBool("quiet") { - writers = append(writers, zerolog.ConsoleWriter{ - Out: os.Stdout, - TimeFormat: time.RFC3339, - }) + handlers = append(handlers, tint.NewHandler(os.Stdout, &tint.Options{ + Level: level, + AddSource: true, + TimeFormat: time.RFC3339Nano, + ReplaceAttr: func(groups []string, attr slog.Attr) slog.Attr { + if attr.Key == slog.LevelKey { + level := attr.Value.Any().(slog.Level) + if level == slog.LevelDebug { + attr.Value = slog.StringValue(ansiBrightMagenta + "DBG" + ansiReset) + } + } else if attr.Key == slog.MessageKey { + attr.Value = slog.StringValue(ansiBold + ansiWhite + fmt.Sprint(attr.Value.Any()) + ansiReset) + } + return attr + }, + })) } if viper.GetString("log-file") != "" { logFile, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o777) if err != nil { - return errors.Wrap(err, "failed to open log file") + return fmt.Errorf("failed to open log file: %w", err) } - writers = append(writers, logFile) + handlers = append(handlers, slog.NewJSONHandler(logFile, &slog.HandlerOptions{})) } - log.Logger = zerolog.New(io.MultiWriter(writers...)).With().Timestamp().Logger() + slog.SetDefault(slog.New( + slogmulti.Fanout(handlers...), + )) return nil }, diff --git a/cmd/search.go b/cmd/search.go index be5e2ac..0f9fca2 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -58,7 +57,7 @@ var searchCmd = &cobra.Command{ case "json": result, err := json.MarshalIndent(modList, "", " ") if err != nil { - return errors.Wrap(err, "failed converting mods to json") + return fmt.Errorf("failed converting mods to json: %w", err) } println(string(result)) } diff --git a/cmd/smr/upload.go b/cmd/smr/upload.go index 0c1ef95..a8bfaeb 100644 --- a/cmd/smr/upload.go +++ b/cmd/smr/upload.go @@ -3,7 +3,10 @@ package smr import ( "bytes" "encoding/json" + "errors" + "fmt" "io" + "log/slog" "math" "mime/multipart" "net/http" @@ -12,8 +15,6 @@ import ( "strings" "time" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -57,7 +58,7 @@ var uploadCmd = &cobra.Command{ stat, err := os.Stat(filePath) if err != nil { - return errors.Wrap(err, "failed to stat file") + return fmt.Errorf("failed to stat file: %w", err) } global, err := cli.InitCLI(true) @@ -71,31 +72,31 @@ var uploadCmd = &cobra.Command{ // TODO Validate .smod file before upload - logBase := log.With().Str("mod-id", modID).Str("path", filePath).Logger() - logBase.Info().Msg("creating a new mod version") + logBase := slog.With(slog.String("mod-id", modID), slog.String("path", filePath)) + logBase.Info("creating a new mod version") createdVersion, err := ficsit.CreateVersion(cmd.Context(), global.APIClient, modID) if err != nil { return err } - logBase = logBase.With().Str("version-id", createdVersion.GetVersionID()).Logger() - logBase.Info().Msg("received version id") + logBase = logBase.With(slog.String("version-id", createdVersion.GetVersionID())) + logBase.Info("received version id") // TODO Parallelize chunk uploading chunkCount := int(math.Ceil(float64(stat.Size()) / float64(chunkSize))) for i := 0; i < chunkCount; i++ { - chunkLog := logBase.With().Int("chunk", i).Logger() - chunkLog.Info().Msg("uploading chunk") + chunkLog := logBase.With(slog.Int("chunk", i)) + chunkLog.Info("uploading chunk") f, err := os.Open(filePath) if err != nil { - return errors.Wrap(err, "failed to open file") + return fmt.Errorf("failed to open file: %w", err) } offset := int64(i) * chunkSize if _, err := f.Seek(offset, 0); err != nil { - return errors.Wrap(err, "failed to seek to chunk offset") + return fmt.Errorf("failed to seek to chunk offset: %w", err) } bufferSize := chunkSize @@ -105,7 +106,7 @@ var uploadCmd = &cobra.Command{ chunk := make([]byte, bufferSize) if _, err := f.Read(chunk); err != nil { - return errors.Wrap(err, "failed to read from chunk offset") + return fmt.Errorf("failed to read from chunk offset: %w", err) } operationBody, err := json.Marshal(map[string]interface{}{ @@ -118,14 +119,14 @@ var uploadCmd = &cobra.Command{ }, }) if err != nil { - return errors.Wrap(err, "failed to serialize operation body") + return fmt.Errorf("failed to serialize operation body: %w", err) } mapBody, err := json.Marshal(map[string]interface{}{ "0": []string{"variables.file"}, }) if err != nil { - return errors.Wrap(err, "failed to serialize map body") + return fmt.Errorf("failed to serialize map body: %w", err) } body := &bytes.Buffer{} @@ -133,33 +134,33 @@ var uploadCmd = &cobra.Command{ operations, err := writer.CreateFormField("operations") if err != nil { - return errors.Wrap(err, "failed to create operations field") + return fmt.Errorf("failed to create operations field: %w", err) } if _, err := operations.Write(operationBody); err != nil { - return errors.Wrap(err, "failed to write to operation field") + return fmt.Errorf("failed to write to operation field: %w", err) } mapField, err := writer.CreateFormField("map") if err != nil { - return errors.Wrap(err, "failed to create map field") + return fmt.Errorf("failed to create map field: %w", err) } if _, err := mapField.Write(mapBody); err != nil { - return errors.Wrap(err, "failed to write to map field") + return fmt.Errorf("failed to write to map field: %w", err) } part, err := writer.CreateFormFile("0", filepath.Base(filePath)) if err != nil { - return errors.Wrap(err, "failed to create file field") + return fmt.Errorf("failed to create file field: %w", err) } if _, err := io.Copy(part, bytes.NewReader(chunk)); err != nil { - return errors.Wrap(err, "failed to write to file field") + return fmt.Errorf("failed to write to file field: %w", err) } if err := writer.Close(); err != nil { - return errors.Wrap(err, "failed to close body writer") + return fmt.Errorf("failed to close body writer: %w", err) } r, _ := http.NewRequest("POST", viper.GetString("api-base")+viper.GetString("graphql-api"), body) @@ -168,12 +169,12 @@ var uploadCmd = &cobra.Command{ client := &http.Client{} if _, err := client.Do(r); err != nil { - return errors.Wrap(err, "failed to execute request") + return fmt.Errorf("failed to execute request: %w", err) } } - logBase.Info().Msg("finalizing uploaded version") + logBase.Info("finalizing uploaded version") finalizeSuccess, err := ficsit.FinalizeCreateVersion(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID(), ficsit.NewVersion{ Changelog: changelog, @@ -184,16 +185,16 @@ var uploadCmd = &cobra.Command{ } if !finalizeSuccess.GetSuccess() { - logBase.Error().Msg("failed to finalize version upload") + logBase.Error("failed to finalize version upload") } time.Sleep(time.Second * 1) for { - logBase.Info().Msg("checking version upload state") + logBase.Info("checking version upload state") state, err := ficsit.CheckVersionUploadState(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID()) if err != nil { - logBase.Err(err).Msg("failed to upload mod") + logBase.Error("failed to upload mod", slog.Any("err", err)) return nil } @@ -203,11 +204,11 @@ var uploadCmd = &cobra.Command{ } if state.GetState().Auto_approved { - logBase.Info().Msg("version successfully uploaded and auto-approved") + logBase.Info("version successfully uploaded and auto-approved") break } - logBase.Info().Msg("version successfully uploaded, but has to be scanned for viruses, which may take up to 15 minutes") + logBase.Info("version successfully uploaded, but has to be scanned for viruses, which may take up to 15 minutes") break } diff --git a/ficsit/root.go b/ficsit/root.go index eb9c2b5..1f4c124 100644 --- a/ficsit/root.go +++ b/ficsit/root.go @@ -1,10 +1,10 @@ package ficsit import ( + "fmt" "net/http" "github.com/Khan/genqlient/graphql" - "github.com/pkg/errors" "github.com/spf13/viper" ) @@ -19,7 +19,11 @@ func (t *AuthedTransport) RoundTrip(req *http.Request) (*http.Response, error) { } rt, err := t.Wrapped.RoundTrip(req) - return rt, errors.Wrap(err, "failed roundtrip") + if err != nil { + return nil, fmt.Errorf("failed roundtrip: %w", err) + } + + return rt, nil } func InitAPI() graphql.Client { diff --git a/ficsit/utils/json.go b/ficsit/utils/json.go index 0762b28..be3e322 100644 --- a/ficsit/utils/json.go +++ b/ficsit/utils/json.go @@ -2,9 +2,8 @@ package utils import ( "bytes" + "fmt" "time" - - "github.com/pkg/errors" ) //goland:noinspection GoUnusedExportedFunction @@ -18,7 +17,7 @@ func UnmarshalDateTime(b []byte, v *time.Time) error { parsed, err := time.Parse(time.RFC3339, string(trimmed)) if err != nil { - return errors.Wrap(err, "failed to parse date time") + return fmt.Errorf("failed to parse date time: %w", err) } *v = parsed diff --git a/go.mod b/go.mod index 4b28ed7..2b7a958 100644 --- a/go.mod +++ b/go.mod @@ -9,21 +9,21 @@ require ( github.com/Khan/genqlient v0.6.0 github.com/MarvinJWendt/testza v0.5.2 github.com/PuerkitoBio/goquery v1.8.1 - github.com/charmbracelet/bubbles v0.16.1 - github.com/charmbracelet/bubbletea v0.24.2 + github.com/charmbracelet/bubbles v0.17.1 + github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/glamour v0.6.0 github.com/charmbracelet/lipgloss v0.9.1 - github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7 + github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d github.com/jlaffaye/ftp v0.2.0 + github.com/lmittmann/tint v1.0.3 github.com/muesli/reflow v0.3.0 - github.com/pkg/errors v0.9.1 github.com/pterm/pterm v0.12.71 github.com/puzpuzpuz/xsync/v3 v3.0.2 - github.com/rs/zerolog v1.31.0 - github.com/sahilm/fuzzy v0.1.0 + github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f + github.com/samber/slog-multi v1.0.2 github.com/satisfactorymodding/ficsit-resolver v0.0.2 github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.18.0 + github.com/spf13/viper v1.18.1 golang.org/x/sync v0.5.0 ) @@ -39,7 +39,7 @@ require ( github.com/andybalholm/cascadia v1.3.2 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/aymanbagabas/go-udiff v0.1.0 // indirect + github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect @@ -57,7 +57,6 @@ require ( github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect @@ -69,10 +68,12 @@ require ( github.com/muesli/termenv v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/samber/lo v1.38.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect diff --git a/go.sum b/go.sum index a500505..2521c59 100644 --- a/go.sum +++ b/go.sum @@ -43,28 +43,27 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/aymanbagabas/go-udiff v0.1.0 h1:9Dpklm2oBBhMxIFbMffmPvDaF7vOYfv9B5HXVr42KMU= -github.com/aymanbagabas/go-udiff v0.1.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs= github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= -github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= -github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= -github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= -github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= +github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= +github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7 h1:lS677S/t1CUy7eq96Z2ZRt0Aqwgq+tshojIBcqDzrtw= -github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7/go.mod h1:TckAxPtan3aJ5wbTgBkySpc50SZhXJRZ8PtYICnZJEw= +github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d h1:J6mdY8xl7YVGMSbPlqDcg64/J3m7wPuX1OWzPMWW4OA= +github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d/go.mod h1:43J0pdacLjJQtomu7vU6RFZX3bn84toqNw7hjX8bhmM= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -80,7 +79,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= @@ -117,14 +115,13 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lmittmann/tint v1.0.3 h1:W5PHeA2D8bBJVvabNfQD/XW9HPLZK1XoPZH0cq8NouQ= +github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -178,17 +175,18 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/slog-multi v1.0.2 h1:6BVH9uHGAsiGkbbtQgAOQJMpKgV8unMrHhhJaw+X1EQ= +github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo= github.com/satisfactorymodding/ficsit-resolver v0.0.2 h1:dj/OsDLpaMUqCHpfBVHvDMUv2nf5gT4HS2ydBMkmtcQ= github.com/satisfactorymodding/ficsit-resolver v0.0.2/go.mod h1:ckKMmMvDoYbbkEbWXEsMes608uvv6EKphXPhHX8LKSc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= @@ -207,8 +205,8 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.0 h1:pN6W1ub/G4OfnM+NR9p7xP9R6TltLUzp5JG9yZD3Qg0= -github.com/spf13/viper v1.18.0/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM= +github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -282,7 +280,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/tea/root.go b/tea/root.go index dbbb4c6..bea47d8 100644 --- a/tea/root.go +++ b/tea/root.go @@ -1,10 +1,11 @@ package tea import ( + "fmt" + "github.com/Khan/genqlient/graphql" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/pkg/errors" resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/spf13/viper" @@ -45,7 +46,7 @@ func (m *rootModel) SetCurrentProfile(profile *cli.Profile) error { if m.GetCurrentInstallation() != nil { if err := m.GetCurrentInstallation().SetProfile(m.global, profile.Name); err != nil { - return errors.Wrap(err, "failed setting profile on installation") + return fmt.Errorf("failed setting profile on installation: %w", err) } } @@ -92,7 +93,7 @@ func (m *rootModel) GetGlobal() *cli.GlobalContext { func RunTea(global *cli.GlobalContext) error { if _, err := tea.NewProgram(scenes.NewMainMenu(newModel(global)), tea.WithAltScreen(), tea.WithMouseCellMotion()).Run(); err != nil { - return errors.Wrap(err, "internal tea error") + return fmt.Errorf("internal tea error: %w", err) } return nil } diff --git a/tea/scenes/installation/new_installation.go b/tea/scenes/installation/new_installation.go index 0a8bbdc..6d624aa 100644 --- a/tea/scenes/installation/new_installation.go +++ b/tea/scenes/installation/new_installation.go @@ -27,11 +27,11 @@ var _ tea.Model = (*newInstallation)(nil) type newInstallation struct { dirList list.Model - input textinput.Model root components.RootModel parent tea.Model error *components.ErrorComponent title string + input textinput.Model } func NewNewInstallation(root components.RootModel, parent tea.Model) tea.Model { diff --git a/tea/scenes/main_menu.go b/tea/scenes/main_menu.go index 21bf546..1bc5830 100644 --- a/tea/scenes/main_menu.go +++ b/tea/scenes/main_menu.go @@ -1,6 +1,7 @@ package scenes import ( + "log/slog" "strings" "time" @@ -8,7 +9,6 @@ import ( "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/rs/zerolog/log" "github.com/spf13/viper" "github.com/satisfactorymodding/ficsit-cli/tea/components" @@ -114,7 +114,7 @@ func NewMainMenu(root components.RootModel) tea.Model { ItemTitle: "Apply Changes", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { if err := root.GetGlobal().Save(); err != nil { - log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err)) errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) currentModel.error = errorComponent return currentModel, cmd @@ -128,7 +128,7 @@ func NewMainMenu(root components.RootModel) tea.Model { ItemTitle: "Save", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { if err := root.GetGlobal().Save(); err != nil { - log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err)) errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) currentModel.error = errorComponent return currentModel, cmd diff --git a/tea/scenes/mods/mod.go b/tea/scenes/mods/mod.go index 8ffdc37..9462c10 100644 --- a/tea/scenes/mods/mod.go +++ b/tea/scenes/mods/mod.go @@ -1,13 +1,13 @@ package mods import ( + "log/slog" "time" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/rs/zerolog/log" "github.com/satisfactorymodding/ficsit-cli/tea/components" "github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors" @@ -72,7 +72,7 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea. Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) { err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0") if err != nil { - log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err)) cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod) return currentModel, cmd } diff --git a/tea/scenes/mods/mod_info.go b/tea/scenes/mods/mod_info.go index cd51568..aa6adb3 100644 --- a/tea/scenes/mods/mod_info.go +++ b/tea/scenes/mods/mod_info.go @@ -2,6 +2,7 @@ package mods import ( "context" + "log/slog" "strconv" "strings" "time" @@ -15,7 +16,6 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" "github.com/charmbracelet/lipgloss" - "github.com/rs/zerolog/log" "github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/tea/components" @@ -192,13 +192,13 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) { markdownDescription, err := converter.ConvertString(mod.Full_description) if err != nil { - log.Error().Err(err).Msg("failed to convert html to markdown") + slog.Error("failed to convert html to markdown", slog.Any("err", err)) markdownDescription = mod.Full_description } description, err := glamour.Render(markdownDescription, "dark") if err != nil { - log.Error().Err(err).Msg("failed to render markdown") + slog.Error("failed to render markdown", slog.Any("err", err)) description = mod.Full_description } diff --git a/tea/scenes/mods/mod_semver.go b/tea/scenes/mods/mod_semver.go index 63bca69..137f299 100644 --- a/tea/scenes/mods/mod_semver.go +++ b/tea/scenes/mods/mod_semver.go @@ -15,12 +15,12 @@ import ( var _ tea.Model = (*modSemver)(nil) type modSemver struct { - input textinput.Model root components.RootModel parent tea.Model error *components.ErrorComponent mod utils.Mod title string + input textinput.Model } func NewModSemver(root components.RootModel, parent tea.Model, mod utils.Mod) tea.Model { diff --git a/tea/scenes/mods/mod_version.go b/tea/scenes/mods/mod_version.go index f7607e5..9912910 100644 --- a/tea/scenes/mods/mod_version.go +++ b/tea/scenes/mods/mod_version.go @@ -1,13 +1,13 @@ package mods import ( + "log/slog" "time" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/rs/zerolog/log" "github.com/satisfactorymodding/ficsit-cli/tea/components" "github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors" @@ -53,7 +53,7 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t Activate: func(msg tea.Msg, currentModel modVersionMenu) (tea.Model, tea.Cmd) { err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0") if err != nil { - log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err)) cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod) return currentModel, cmd } diff --git a/tea/scenes/profile/new_profile.go b/tea/scenes/profile/new_profile.go index 48c1eee..fcc69d2 100644 --- a/tea/scenes/profile/new_profile.go +++ b/tea/scenes/profile/new_profile.go @@ -33,13 +33,13 @@ func (k keyMap) FullHelp() [][]key.Binding { } type newProfile struct { - input textinput.Model root components.RootModel parent tea.Model error *components.ErrorComponent - title string help help.Model + title string keys keyMap + input textinput.Model } func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model { diff --git a/tea/scenes/profile/rename_profile.go b/tea/scenes/profile/rename_profile.go index 86fdd2c..99420ad 100644 --- a/tea/scenes/profile/rename_profile.go +++ b/tea/scenes/profile/rename_profile.go @@ -17,12 +17,12 @@ import ( var _ tea.Model = (*renameProfile)(nil) type renameProfile struct { - input textinput.Model root components.RootModel parent tea.Model error *components.ErrorComponent title string oldName string + input textinput.Model } func NewRenameProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model { diff --git a/tea/tea_test.go b/tea/tea_test.go index 0a87390..c619a9a 100644 --- a/tea/tea_test.go +++ b/tea/tea_test.go @@ -1,6 +1,7 @@ package tea import ( + "errors" "io" "os" "runtime" @@ -11,7 +12,6 @@ import ( "github.com/MarvinJWendt/testza" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/x/exp/teatest" - "github.com/pkg/errors" "github.com/satisfactorymodding/ficsit-cli/cfg" "github.com/satisfactorymodding/ficsit-cli/cli" diff --git a/utils/io.go b/utils/io.go index b93b5b9..2e638a0 100644 --- a/utils/io.go +++ b/utils/io.go @@ -4,14 +4,13 @@ import ( "archive/zip" "crypto/sha256" "encoding/hex" + "fmt" "io" "os" "path/filepath" "sync" "sync/atomic" - "github.com/pkg/errors" - "github.com/satisfactorymodding/ficsit-cli/cli/disk" ) @@ -51,13 +50,17 @@ func (pt *Progresser) Read(p []byte) (int, error) { return n, io.EOF } - return n, errors.Wrap(err, "failed to read") + if err != nil { + return 0, fmt.Errorf("failed to read: %w", err) + } + + return n, nil } func SHA256Data(f io.Reader) (string, error) { h := sha256.New() if _, err := io.Copy(h, f); err != nil { - return "", errors.Wrap(err, "failed to compute hash") + return "", fmt.Errorf("failed to compute hash: %w", err) } return hex.EncodeToString(h.Sum(nil)), nil @@ -68,7 +71,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates hashBytes, err := d.Read(hashFile) if err != nil { if !d.IsNotExist(err) { - return errors.Wrap(err, "failed to read .smm mod hash file") + return fmt.Errorf("failed to read .smm mod hash file: %w", err) } } else { if hash == string(hashBytes) { @@ -78,21 +81,21 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates if err := d.MkDir(location); err != nil { if !d.IsExist(err) { - return errors.Wrap(err, "failed to create mod directory: "+location) + return fmt.Errorf("failed to create mod directory: %s: %w", location, err) } if err := d.Remove(location); err != nil { - return errors.Wrap(err, "failed to remove directory: "+location) + return fmt.Errorf("failed to remove directory: %s: %w", location, err) } if err := d.MkDir(location); err != nil { - return errors.Wrap(err, "failed to create mod directory: "+location) + return fmt.Errorf("failed to create mod directory: %s: %w", location, err) } } reader, err := zip.NewReader(f, size) if err != nil { - return errors.Wrap(err, "failed to read file as zip") + return fmt.Errorf("failed to read file as zip: %w", err) } totalSize := int64(0) @@ -117,7 +120,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates outFileLocation := filepath.Join(location, file.Name) if err := d.MkDir(filepath.Dir(outFileLocation)); err != nil { - return errors.Wrap(err, "failed to create mod directory: "+location) + return fmt.Errorf("failed to create mod directory: %s: %w", location, err) } var fileUpdates chan GenericProgress @@ -144,7 +147,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates } if err := d.Write(hashFile, []byte(hash)); err != nil { - return errors.Wrap(err, "failed to write .smm mod hash file") + return fmt.Errorf("failed to write .smm mod hash file: %w", err) } if updates != nil { @@ -161,14 +164,14 @@ func writeZipFile(outFileLocation string, file *zip.File, d disk.Disk, updates c outFile, err := d.Open(outFileLocation, os.O_CREATE|os.O_RDWR) if err != nil { - return errors.Wrap(err, "failed to write to file: "+outFileLocation) + return fmt.Errorf("failed to write to file: %s: %w", outFileLocation, err) } defer outFile.Close() inFile, err := file.Open() if err != nil { - return errors.Wrap(err, "failed to process mod zip") + return fmt.Errorf("failed to process mod zip: %w", err) } defer inFile.Close() @@ -179,7 +182,7 @@ func writeZipFile(outFileLocation string, file *zip.File, d disk.Disk, updates c } if _, err := io.Copy(outFile, progressInReader); err != nil { - return errors.Wrap(err, "failed to write to file: "+outFileLocation) + return fmt.Errorf("failed to write to file: %s: %w", outFileLocation, err) } return nil diff --git a/utils/structures.go b/utils/structures.go index 6ca1146..fbd93ac 100644 --- a/utils/structures.go +++ b/utils/structures.go @@ -2,19 +2,18 @@ package utils import ( "encoding/json" - - "github.com/pkg/errors" + "fmt" ) func Copy[T any](obj T) (*T, error) { marshal, err := json.Marshal(obj) if err != nil { - return nil, errors.Wrap(err, "failed to marshal object") + return nil, fmt.Errorf("failed to marshal object: %w", err) } out := new(T) if err := json.Unmarshal(marshal, out); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal object") + return nil, fmt.Errorf("failed to unmarshal object: %w", err) } return out, nil