Removes the banned `fmt` import from profile_manager.go. All four fmt.Errorf calls are replaced with ErrInternal/ErrProfileNotFound, consistent with the error pattern used throughout the rest of the mining package. Co-Authored-By: Charon <charon@lethean.io>
166 lines
4.7 KiB
Go
166 lines
4.7 KiB
Go
package mining
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/adrg/xdg"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const profileConfigFileName = "mining_profiles.json"
|
|
|
|
// profileManager, err := NewProfileManager()
|
|
// profileManager.CreateProfile(profile)
|
|
type ProfileManager struct {
|
|
mutex sync.RWMutex
|
|
profiles map[string]*MiningProfile
|
|
configPath string
|
|
}
|
|
|
|
// profileManager, err := NewProfileManager()
|
|
// if err != nil { return err }
|
|
func NewProfileManager() (*ProfileManager, error) {
|
|
configPath, err := xdg.ConfigFile(filepath.Join("lethean-desktop", profileConfigFileName))
|
|
if err != nil {
|
|
return nil, ErrInternal("could not resolve config path").WithCause(err)
|
|
}
|
|
|
|
profileManager := &ProfileManager{
|
|
profiles: make(map[string]*MiningProfile),
|
|
configPath: configPath,
|
|
}
|
|
|
|
if err := profileManager.loadProfiles(); err != nil {
|
|
// If the file doesn't exist, that's fine, but any other error is a problem.
|
|
if !os.IsNotExist(err) {
|
|
return nil, ErrInternal("could not load profiles").WithCause(err)
|
|
}
|
|
}
|
|
|
|
return profileManager, nil
|
|
}
|
|
|
|
// profileManager.loadProfiles() // call after acquiring profileManager.mutex.Lock() if needed externally
|
|
func (profileManager *ProfileManager) loadProfiles() error {
|
|
profileManager.mutex.Lock()
|
|
defer profileManager.mutex.Unlock()
|
|
|
|
data, err := os.ReadFile(profileManager.configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var profiles []*MiningProfile
|
|
if err := json.Unmarshal(data, &profiles); err != nil {
|
|
return err
|
|
}
|
|
|
|
profileManager.profiles = make(map[string]*MiningProfile)
|
|
for _, profile := range profiles {
|
|
profileManager.profiles[profile.ID] = profile
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// profileManager.saveProfiles() // caller must hold profileManager.mutex before calling; uses atomic write (temp→sync→rename)
|
|
func (profileManager *ProfileManager) saveProfiles() error {
|
|
profileList := make([]*MiningProfile, 0, len(profileManager.profiles))
|
|
for _, profile := range profileManager.profiles {
|
|
profileList = append(profileList, profile)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(profileList, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return AtomicWriteFile(profileManager.configPath, data, 0600)
|
|
}
|
|
|
|
// created, err := profileManager.CreateProfile(&MiningProfile{Name: "XMRig CPU", MinerType: "xmrig"})
|
|
func (profileManager *ProfileManager) CreateProfile(profile *MiningProfile) (*MiningProfile, error) {
|
|
profileManager.mutex.Lock()
|
|
defer profileManager.mutex.Unlock()
|
|
|
|
profile.ID = uuid.New().String()
|
|
profileManager.profiles[profile.ID] = profile
|
|
|
|
if err := profileManager.saveProfiles(); err != nil {
|
|
// Rollback
|
|
delete(profileManager.profiles, profile.ID)
|
|
return nil, err
|
|
}
|
|
|
|
return profile, nil
|
|
}
|
|
|
|
// profile, ok := profileManager.GetProfile("abc-123")
|
|
// if !ok { return core.E("profile.Get", "profile not found", nil) }
|
|
func (profileManager *ProfileManager) GetProfile(id string) (*MiningProfile, bool) {
|
|
profileManager.mutex.RLock()
|
|
defer profileManager.mutex.RUnlock()
|
|
profile, exists := profileManager.profiles[id]
|
|
return profile, exists
|
|
}
|
|
|
|
// profiles := profileManager.GetAllProfiles()
|
|
// for _, profile := range profiles { render(profile) }
|
|
func (profileManager *ProfileManager) GetAllProfiles() []*MiningProfile {
|
|
profileManager.mutex.RLock()
|
|
defer profileManager.mutex.RUnlock()
|
|
|
|
profileList := make([]*MiningProfile, 0, len(profileManager.profiles))
|
|
for _, profile := range profileManager.profiles {
|
|
profileList = append(profileList, profile)
|
|
}
|
|
return profileList
|
|
}
|
|
|
|
// profile.Name = "XMRig GPU"
|
|
// if err := profileManager.UpdateProfile(profile); err != nil { return err }
|
|
func (profileManager *ProfileManager) UpdateProfile(profile *MiningProfile) error {
|
|
profileManager.mutex.Lock()
|
|
defer profileManager.mutex.Unlock()
|
|
|
|
oldProfile, exists := profileManager.profiles[profile.ID]
|
|
if !exists {
|
|
return ErrProfileNotFound(profile.ID)
|
|
}
|
|
|
|
// Update in-memory state
|
|
profileManager.profiles[profile.ID] = profile
|
|
|
|
// Save to disk - rollback if save fails
|
|
if err := profileManager.saveProfiles(); err != nil {
|
|
// Restore old profile on save failure
|
|
profileManager.profiles[profile.ID] = oldProfile
|
|
return ErrInternal("failed to save profile").WithCause(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// if err := profileManager.DeleteProfile("abc-123"); err != nil { return err }
|
|
func (profileManager *ProfileManager) DeleteProfile(id string) error {
|
|
profileManager.mutex.Lock()
|
|
defer profileManager.mutex.Unlock()
|
|
|
|
profile, exists := profileManager.profiles[id]
|
|
if !exists {
|
|
return ErrProfileNotFound(id)
|
|
}
|
|
delete(profileManager.profiles, id)
|
|
|
|
// Save to disk - rollback if save fails
|
|
if err := profileManager.saveProfiles(); err != nil {
|
|
// Restore profile on save failure
|
|
profileManager.profiles[id] = profile
|
|
return ErrInternal("failed to delete profile").WithCause(err)
|
|
}
|
|
|
|
return nil
|
|
}
|