123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- package main
- import (
- "cmp"
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "slices"
- "strings"
- "sync"
- )
- var (
- ContactsFile = "contacts.json"
- )
- type (
- Contacts []Contact
- ContactsStore struct {
- sync.RWMutex
- data map[int]Contact
- }
- )
- func NewContactsStore() (*ContactsStore, error) {
- cs := ContactsStore{
- data: make(map[int]Contact),
- }
- if err := cs.Load(); err != nil {
- return nil, err
- }
- return &cs, nil
- }
- func (cs *ContactsStore) Get(search string) Contacts {
- contacts := cs.GetAll()
- if search == "" {
- return contacts
- }
- return slices.DeleteFunc(contacts, func(c Contact) bool {
- return (c.First == "" || !strings.Contains(c.First, search)) &&
- (c.Last == "" || !strings.Contains(c.Last, search)) &&
- (c.Email == "" || !strings.Contains(c.Email, search)) &&
- (c.Phone == "" || !strings.Contains(c.Phone, search))
- })
- }
- func MapValues[M ~map[K]V, K comparable, V any](m M) []V {
- sl := make([]V, 0, len(m))
- for _, v := range m {
- sl = append(sl, v)
- }
- return sl
- }
- func (cs *ContactsStore) GetAll() Contacts {
- cs.RLock()
- defer cs.RUnlock()
- contacts := MapValues(cs.data)
- slices.SortStableFunc(contacts, func(a, b Contact) int {
- if sgn := cmp.Compare(a.First, b.First); sgn != 0 {
- return sgn
- }
- if sgn := cmp.Compare(a.Last, b.Last); sgn != 0 {
- return sgn
- }
- if sgn := cmp.Compare(a.Phone, b.Phone); sgn != 0 {
- return sgn
- }
- if sgn := cmp.Compare(a.Email, b.Email); sgn != 0 {
- return sgn
- }
- return cmp.Compare(a.ID, b.ID)
- })
- return contacts
- }
- func (cs *ContactsStore) Load() error {
- if cs == nil {
- return errors.New("cannot load into nil store")
- }
- bs, err := os.ReadFile(ContactsFile)
- if err != nil {
- return fmt.Errorf("reading file: %w", err)
- }
- contacts := make(Contacts, 0)
- if err := json.Unmarshal(bs, &contacts); err != nil {
- return fmt.Errorf("unmarshalling file: %w", err)
- }
- cs.Lock()
- defer cs.Unlock()
- for _, contact := range contacts {
- cs.data[contact.ID] = contact
- }
- return nil
- }
- type Contact struct {
- ID int `json:"id"`
- First string `json:"first"`
- Last string `json:"last"`
- Phone string `json:"phone"`
- Email string `json:"email"`
- errors map[string]string
- }
|