From 9ec95e1dbb9da2ff636cfc6db7835e3bfc1742cd Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Fri, 9 Feb 2024 13:58:26 -0600 Subject: [PATCH] feat: package manager state support Fixes #18 --- pkgmgr/env.go | 5 +++ pkgmgr/pkgmgr.go | 24 ++++++++++++- pkgmgr/state.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 pkgmgr/env.go create mode 100644 pkgmgr/state.go diff --git a/pkgmgr/env.go b/pkgmgr/env.go new file mode 100644 index 0000000..85fd6d4 --- /dev/null +++ b/pkgmgr/env.go @@ -0,0 +1,5 @@ +package pkgmgr + +type Environment struct { + // TODO +} diff --git a/pkgmgr/pkgmgr.go b/pkgmgr/pkgmgr.go index 5e25787..e64f999 100644 --- a/pkgmgr/pkgmgr.go +++ b/pkgmgr/pkgmgr.go @@ -14,14 +14,30 @@ package pkgmgr +import ( + "errors" + "fmt" +) + +// ErrOperationFailed is a placeholder error for operations that directly log errors. +// It's used to signify when an operation has failed when the actual error message is +// sent through the provided logger +var ErrOperationFailed = errors.New("the operation has failed") + type PackageManager struct { config Config + state *State // TODO } func NewPackageManager(cfg Config) (*PackageManager, error) { + // Make sure that a logger was provided, since we use it for pretty much all feedback + if cfg.Logger == nil { + return nil, errors.New("you must provide a logger") + } p := &PackageManager{ config: cfg, + state: NewState(cfg), } if err := p.init(); err != nil { return nil, err @@ -38,7 +54,13 @@ func NewDefaultPackageManager() (*PackageManager, error) { } func (p *PackageManager) init() error { - // TODO: create config/cache dirs p.config.Logger.Debug("initializing package manager") + if err := p.state.Load(); err != nil { + return fmt.Errorf("failed to load state: %s", err) + } + // TODO: remove me + if err := p.state.Save(); err != nil { + return err + } return nil } diff --git a/pkgmgr/state.go b/pkgmgr/state.go new file mode 100644 index 0000000..caf647a --- /dev/null +++ b/pkgmgr/state.go @@ -0,0 +1,92 @@ +package pkgmgr + +import ( + "encoding/json" + "os" + "path/filepath" +) + +const ( + environmentsFilename = "environments.json" +) + +type State struct { + config Config + Environments []Environment + // TODO: installed packages +} + +func NewState(cfg Config) *State { + return &State{ + config: cfg, + Environments: make([]Environment, 0), + } +} + +func (s *State) Load() error { + if err := s.loadEnvironments(); err != nil { + return err + } + return nil +} + +func (s *State) Save() error { + if err := s.saveEnvironments(); err != nil { + return err + } + return nil +} + +func (s *State) loadFile(filename string, dest any) error { + tmpPath := filepath.Join( + s.config.ConfigDir, + filename, + ) + // Check if the file exists and we can access it + if _, err := os.Stat(tmpPath); err != nil { + // Treat no file like an empty file + if os.IsNotExist(err) { + return nil + } + return err + } + content, err := os.ReadFile(tmpPath) + if err != nil { + return err + } + if err := json.Unmarshal(content, dest); err != nil { + return err + } + return nil +} + +func (s *State) saveFile(filename string, src any) error { + // Create parent directory if it doesn't exist + if _, err := os.Stat(s.config.ConfigDir); err != nil { + if os.IsNotExist(err) { + if err := os.MkdirAll(s.config.ConfigDir, os.ModePerm); err != nil { + return err + } + } + } + tmpPath := filepath.Join( + s.config.ConfigDir, + filename, + ) + jsonContent, err := json.Marshal(src) + if err != nil { + return err + } + if err := os.WriteFile(tmpPath, jsonContent, os.ModePerm); err != nil { + return err + } + return nil +} + +func (s *State) loadEnvironments() error { + return s.loadFile(environmentsFilename, &(s.Environments)) +} + +func (s *State) saveEnvironments() error { + return s.saveFile(environmentsFilename, &(s.Environments)) +}