contacts_model.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package main
  2. import (
  3. "cmp"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "slices"
  9. "strings"
  10. "sync"
  11. )
  12. var (
  13. ContactsFile = "contacts.json"
  14. )
  15. type (
  16. Contacts []Contact
  17. ContactsStore struct {
  18. sync.RWMutex
  19. data map[int]Contact
  20. }
  21. )
  22. func NewContactsStore() (*ContactsStore, error) {
  23. cs := ContactsStore{
  24. data: make(map[int]Contact),
  25. }
  26. if err := cs.Load(); err != nil {
  27. return nil, err
  28. }
  29. return &cs, nil
  30. }
  31. func (cs *ContactsStore) Get(search string) Contacts {
  32. contacts := cs.GetAll()
  33. if search == "" {
  34. return contacts
  35. }
  36. return slices.DeleteFunc(contacts, func(c Contact) bool {
  37. return (c.First == "" || !strings.Contains(c.First, search)) &&
  38. (c.Last == "" || !strings.Contains(c.Last, search)) &&
  39. (c.Email == "" || !strings.Contains(c.Email, search)) &&
  40. (c.Phone == "" || !strings.Contains(c.Phone, search))
  41. })
  42. }
  43. func MapValues[M ~map[K]V, K comparable, V any](m M) []V {
  44. sl := make([]V, 0, len(m))
  45. for _, v := range m {
  46. sl = append(sl, v)
  47. }
  48. return sl
  49. }
  50. func (cs *ContactsStore) GetAll() Contacts {
  51. cs.RLock()
  52. defer cs.RUnlock()
  53. contacts := MapValues(cs.data)
  54. slices.SortStableFunc(contacts, func(a, b Contact) int {
  55. if sgn := cmp.Compare(a.First, b.First); sgn != 0 {
  56. return sgn
  57. }
  58. if sgn := cmp.Compare(a.Last, b.Last); sgn != 0 {
  59. return sgn
  60. }
  61. if sgn := cmp.Compare(a.Phone, b.Phone); sgn != 0 {
  62. return sgn
  63. }
  64. if sgn := cmp.Compare(a.Email, b.Email); sgn != 0 {
  65. return sgn
  66. }
  67. return cmp.Compare(a.ID, b.ID)
  68. })
  69. return contacts
  70. }
  71. func (cs *ContactsStore) Load() error {
  72. if cs == nil {
  73. return errors.New("cannot load into nil store")
  74. }
  75. bs, err := os.ReadFile(ContactsFile)
  76. if err != nil {
  77. return fmt.Errorf("reading file: %w", err)
  78. }
  79. contacts := make(Contacts, 0)
  80. if err := json.Unmarshal(bs, &contacts); err != nil {
  81. return fmt.Errorf("unmarshalling file: %w", err)
  82. }
  83. cs.Lock()
  84. defer cs.Unlock()
  85. for _, contact := range contacts {
  86. cs.data[contact.ID] = contact
  87. }
  88. return nil
  89. }
  90. type Contact struct {
  91. ID int `json:"id"`
  92. First string `json:"first"`
  93. Last string `json:"last"`
  94. Phone string `json:"phone"`
  95. Email string `json:"email"`
  96. errors map[string]string
  97. }