diff options
| author | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2024-07-09 21:36:27 -0700 |
|---|---|---|
| committer | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2024-07-09 21:42:05 -0700 |
| commit | d9bc63a10d0978d9a5222cf7be74044e55f409b7 (patch) | |
| tree | 1797c9593c279bf1334ed8285de8054a92a28dcb /hotline/account_manager.go | |
| parent | 8fa166777cbcd92e871e937d9557f0f1a732c04d (diff) | |
Extensive refactor and clean up
Diffstat (limited to 'hotline/account_manager.go')
| -rw-r--r-- | hotline/account_manager.go | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/hotline/account_manager.go b/hotline/account_manager.go new file mode 100644 index 0000000..769d12c --- /dev/null +++ b/hotline/account_manager.go @@ -0,0 +1,188 @@ +package hotline + +import ( + "fmt" + "github.com/stretchr/testify/mock" + "gopkg.in/yaml.v3" + "os" + "path" + "path/filepath" + "sync" +) + +type AccountManager interface { + Create(account Account) error + Update(account Account, newLogin string) error + Get(login string) *Account + List() []Account + Delete(login string) error +} + +type YAMLAccountManager struct { + accounts map[string]Account + accountDir string + + mu sync.Mutex +} + +func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) { + accountMgr := YAMLAccountManager{ + accountDir: accountDir, + accounts: make(map[string]Account), + } + + matches, err := filepath.Glob(filepath.Join(accountDir, "*.yaml")) + if err != nil { + return nil, err + } + + if len(matches) == 0 { + return nil, fmt.Errorf("no accounts found in directory: %s", accountDir) + } + + for _, file := range matches { + var account Account + if err = loadFromYAMLFile(file, &account); err != nil { + return nil, fmt.Errorf("error loading account %s: %w", file, err) + } + + accountMgr.accounts[account.Login] = account + } + + return &accountMgr, nil +} + +func (am *YAMLAccountManager) Create(account Account) error { + am.mu.Lock() + defer am.mu.Unlock() + + // Create account file, returning an error if one already exists. + file, err := os.OpenFile( + filepath.Join(am.accountDir, path.Join("/", account.Login+".yaml")), + os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644, + ) + if err != nil { + return fmt.Errorf("error creating account file: %w", err) + } + defer file.Close() + + b, err := yaml.Marshal(account) + if err != nil { + return fmt.Errorf("marshal account to YAML: %v", err) + } + + _, err = file.Write(b) + if err != nil { + return fmt.Errorf("write account file: %w", err) + } + + am.accounts[account.Login] = account + + return nil +} + +func (am *YAMLAccountManager) Update(account Account, newLogin string) error { + am.mu.Lock() + defer am.mu.Unlock() + + // If the login has changed, rename the account file. + if account.Login != newLogin { + err := os.Rename( + filepath.Join(am.accountDir, path.Join("/", account.Login)+".yaml"), + filepath.Join(am.accountDir, path.Join("/", newLogin)+".yaml"), + ) + if err != nil { + return fmt.Errorf("error renaming account file: %w", err) + } + + account.Login = newLogin + am.accounts[newLogin] = account + + delete(am.accounts, account.Login) + } + + out, err := yaml.Marshal(&account) + if err != nil { + return err + } + + if err := os.WriteFile(filepath.Join(am.accountDir, newLogin+".yaml"), out, 0644); err != nil { + return fmt.Errorf("error writing account file: %w", err) + } + + am.accounts[account.Login] = account + + return nil +} + +func (am *YAMLAccountManager) Get(login string) *Account { + am.mu.Lock() + defer am.mu.Unlock() + + account, ok := am.accounts[login] + if !ok { + return nil + } + + return &account +} + +func (am *YAMLAccountManager) List() []Account { + am.mu.Lock() + defer am.mu.Unlock() + + var accounts []Account + for _, account := range am.accounts { + accounts = append(accounts, account) + } + + return accounts +} + +func (am *YAMLAccountManager) Delete(login string) error { + am.mu.Lock() + defer am.mu.Unlock() + + err := os.Remove(filepath.Join(am.accountDir, path.Join("/", login+".yaml"))) + if err != nil { + return fmt.Errorf("delete account file: %v", err) + } + + delete(am.accounts, login) + + return nil +} + +type MockAccountManager struct { + mock.Mock +} + +func (m *MockAccountManager) Create(account Account) error { + args := m.Called(account) + + return args.Error(0) +} + +func (m *MockAccountManager) Update(account Account, newLogin string) error { + args := m.Called(account, newLogin) + + return args.Error(0) +} + +func (m *MockAccountManager) Get(login string) *Account { + args := m.Called(login) + + return args.Get(0).(*Account) +} + +func (m *MockAccountManager) List() []Account { + args := m.Called() + + return args.Get(0).([]Account) +} + +func (m *MockAccountManager) Delete(login string) error { + args := m.Called(login) + + return args.Error(0) +} |