Files
pop/internal/contact/manager.go

219 lines
4.3 KiB
Go

package contact
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/frenocorp/pop/internal/config"
)
type ContactManager struct {
mu sync.Mutex
configDir string
contactsFile string
}
func NewContactManager() *ContactManager {
cfg := config.NewConfigManager()
configDir := cfg.ConfigDir()
return &ContactManager{
configDir: configDir,
contactsFile: filepath.Join(configDir, "contacts.json"),
}
}
func (m *ContactManager) List(req ListContactsRequest) (*ListContactsResponse, error) {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return nil, err
}
var filtered []Contact
for _, c := range contacts {
if req.Search != "" {
search := req.Search
if !contains(c.Email, search) && !contains(c.Name, search) {
continue
}
}
if req.IsFavorite != nil && *req.IsFavorite != c.IsFavorite {
continue
}
filtered = append(filtered, c)
}
start := req.Page * req.PageSize
end := start + req.PageSize
if start > len(filtered) {
start = len(filtered)
}
if end > len(filtered) {
end = len(filtered)
}
return &ListContactsResponse{
Total: len(filtered),
Contacts: filtered[start:end],
}, nil
}
func (m *ContactManager) Create(req CreateContactRequest) (*Contact, error) {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return nil, err
}
id := fmt.Sprintf("contact_%d", time.Now().UnixNano())
contact := Contact{
ID: id,
Email: req.Email,
Name: req.Name,
Phone: req.Phone,
Address: req.Address,
Notes: req.Notes,
IsFavorite: req.IsFavorite,
CustomFields: req.CustomFields,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
contacts = append(contacts, contact)
if err := m.saveContacts(contacts); err != nil {
return nil, err
}
return &contact, nil
}
func (m *ContactManager) Get(id string) (*Contact, error) {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return nil, err
}
for _, c := range contacts {
if c.ID == id {
return &c, nil
}
}
return nil, fmt.Errorf("contact not found: %s", id)
}
func (m *ContactManager) Update(id string, req UpdateContactRequest) (*Contact, error) {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return nil, err
}
for i, c := range contacts {
if c.ID == id {
if req.Email != nil {
c.Email = *req.Email
}
if req.Name != nil {
c.Name = *req.Name
}
if req.Phone != nil {
c.Phone = *req.Phone
}
if req.Address != nil {
c.Address = *req.Address
}
if req.Notes != nil {
c.Notes = *req.Notes
}
if req.IsFavorite != nil {
c.IsFavorite = *req.IsFavorite
}
if req.CustomFields != nil {
c.CustomFields = req.CustomFields
}
c.UpdatedAt = time.Now()
contacts[i] = c
if err := m.saveContacts(contacts); err != nil {
return nil, err
}
return &c, nil
}
}
return nil, fmt.Errorf("contact not found: %s", id)
}
func (m *ContactManager) Delete(id string) error {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return err
}
for i, c := range contacts {
if c.ID == id {
contacts = append(contacts[:i], contacts[i+1:]...)
return m.saveContacts(contacts)
}
}
return fmt.Errorf("contact not found: %s", id)
}
func (m *ContactManager) loadContacts() ([]Contact, error) {
data, err := os.ReadFile(m.contactsFile)
if err != nil {
if os.IsNotExist(err) {
return []Contact{}, nil
}
return nil, err
}
var contacts []Contact
if err := json.Unmarshal(data, &contacts); err != nil {
return nil, err
}
return contacts, nil
}
func (m *ContactManager) saveContacts(contacts []Contact) error {
if err := os.MkdirAll(m.configDir, 0755); err != nil {
return err
}
data, err := json.MarshalIndent(contacts, "", " ")
if err != nil {
return err
}
return os.WriteFile(m.contactsFile, data, 0600)
}
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > 0 && len(substr) > 0 &&
(s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
findSubstring(s, substr)))
}
func findSubstring(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}