]>
Commit | Line | Data |
---|---|---|
d9bc63a1 JH |
1 | package hotline |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "github.com/stretchr/testify/mock" | |
6 | "gopkg.in/yaml.v3" | |
7 | "os" | |
8 | "path" | |
9 | "path/filepath" | |
10 | "sync" | |
11 | ) | |
12 | ||
13 | type AccountManager interface { | |
14 | Create(account Account) error | |
15 | Update(account Account, newLogin string) error | |
16 | Get(login string) *Account | |
17 | List() []Account | |
18 | Delete(login string) error | |
19 | } | |
20 | ||
21 | type YAMLAccountManager struct { | |
22 | accounts map[string]Account | |
23 | accountDir string | |
24 | ||
25 | mu sync.Mutex | |
26 | } | |
27 | ||
28 | func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) { | |
29 | accountMgr := YAMLAccountManager{ | |
30 | accountDir: accountDir, | |
31 | accounts: make(map[string]Account), | |
32 | } | |
33 | ||
34 | matches, err := filepath.Glob(filepath.Join(accountDir, "*.yaml")) | |
35 | if err != nil { | |
36 | return nil, err | |
37 | } | |
38 | ||
39 | if len(matches) == 0 { | |
40 | return nil, fmt.Errorf("no accounts found in directory: %s", accountDir) | |
41 | } | |
42 | ||
43 | for _, file := range matches { | |
44 | var account Account | |
45 | if err = loadFromYAMLFile(file, &account); err != nil { | |
46 | return nil, fmt.Errorf("error loading account %s: %w", file, err) | |
47 | } | |
48 | ||
49 | accountMgr.accounts[account.Login] = account | |
50 | } | |
51 | ||
52 | return &accountMgr, nil | |
53 | } | |
54 | ||
55 | func (am *YAMLAccountManager) Create(account Account) error { | |
56 | am.mu.Lock() | |
57 | defer am.mu.Unlock() | |
58 | ||
59 | // Create account file, returning an error if one already exists. | |
60 | file, err := os.OpenFile( | |
61 | filepath.Join(am.accountDir, path.Join("/", account.Login+".yaml")), | |
62 | os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644, | |
63 | ) | |
64 | if err != nil { | |
65 | return fmt.Errorf("error creating account file: %w", err) | |
66 | } | |
67 | defer file.Close() | |
68 | ||
69 | b, err := yaml.Marshal(account) | |
70 | if err != nil { | |
71 | return fmt.Errorf("marshal account to YAML: %v", err) | |
72 | } | |
73 | ||
74 | _, err = file.Write(b) | |
75 | if err != nil { | |
76 | return fmt.Errorf("write account file: %w", err) | |
77 | } | |
78 | ||
79 | am.accounts[account.Login] = account | |
80 | ||
81 | return nil | |
82 | } | |
83 | ||
84 | func (am *YAMLAccountManager) Update(account Account, newLogin string) error { | |
85 | am.mu.Lock() | |
86 | defer am.mu.Unlock() | |
87 | ||
88 | // If the login has changed, rename the account file. | |
89 | if account.Login != newLogin { | |
90 | err := os.Rename( | |
91 | filepath.Join(am.accountDir, path.Join("/", account.Login)+".yaml"), | |
92 | filepath.Join(am.accountDir, path.Join("/", newLogin)+".yaml"), | |
93 | ) | |
94 | if err != nil { | |
95 | return fmt.Errorf("error renaming account file: %w", err) | |
96 | } | |
97 | ||
98 | account.Login = newLogin | |
99 | am.accounts[newLogin] = account | |
100 | ||
101 | delete(am.accounts, account.Login) | |
102 | } | |
103 | ||
104 | out, err := yaml.Marshal(&account) | |
105 | if err != nil { | |
106 | return err | |
107 | } | |
108 | ||
109 | if err := os.WriteFile(filepath.Join(am.accountDir, newLogin+".yaml"), out, 0644); err != nil { | |
110 | return fmt.Errorf("error writing account file: %w", err) | |
111 | } | |
112 | ||
113 | am.accounts[account.Login] = account | |
114 | ||
115 | return nil | |
116 | } | |
117 | ||
118 | func (am *YAMLAccountManager) Get(login string) *Account { | |
119 | am.mu.Lock() | |
120 | defer am.mu.Unlock() | |
121 | ||
122 | account, ok := am.accounts[login] | |
123 | if !ok { | |
124 | return nil | |
125 | } | |
126 | ||
127 | return &account | |
128 | } | |
129 | ||
130 | func (am *YAMLAccountManager) List() []Account { | |
131 | am.mu.Lock() | |
132 | defer am.mu.Unlock() | |
133 | ||
134 | var accounts []Account | |
135 | for _, account := range am.accounts { | |
136 | accounts = append(accounts, account) | |
137 | } | |
138 | ||
139 | return accounts | |
140 | } | |
141 | ||
142 | func (am *YAMLAccountManager) Delete(login string) error { | |
143 | am.mu.Lock() | |
144 | defer am.mu.Unlock() | |
145 | ||
146 | err := os.Remove(filepath.Join(am.accountDir, path.Join("/", login+".yaml"))) | |
147 | if err != nil { | |
148 | return fmt.Errorf("delete account file: %v", err) | |
149 | } | |
150 | ||
151 | delete(am.accounts, login) | |
152 | ||
153 | return nil | |
154 | } | |
155 | ||
156 | type MockAccountManager struct { | |
157 | mock.Mock | |
158 | } | |
159 | ||
160 | func (m *MockAccountManager) Create(account Account) error { | |
161 | args := m.Called(account) | |
162 | ||
163 | return args.Error(0) | |
164 | } | |
165 | ||
166 | func (m *MockAccountManager) Update(account Account, newLogin string) error { | |
167 | args := m.Called(account, newLogin) | |
168 | ||
169 | return args.Error(0) | |
170 | } | |
171 | ||
172 | func (m *MockAccountManager) Get(login string) *Account { | |
173 | args := m.Called(login) | |
174 | ||
175 | return args.Get(0).(*Account) | |
176 | } | |
177 | ||
178 | func (m *MockAccountManager) List() []Account { | |
179 | args := m.Called() | |
180 | ||
181 | return args.Get(0).([]Account) | |
182 | } | |
183 | ||
184 | func (m *MockAccountManager) Delete(login string) error { | |
185 | args := m.Called(login) | |
186 | ||
187 | return args.Error(0) | |
188 | } |