package main import ( "cmp" "encoding/json" "errors" "log" "math/rand" "os" "slices" "strings" "sync" "time" ) // Contact model const ( PageSize = 100 ) type anyMap map[string]any /* # mock contacts database */ var db = make(map[uint64]Contact, 0) /* class Contact: */ type Contact struct { ID uint64 `json:"id"` First string `json:"first"` Last string `json:"last"` Phone string `json:"phone"` Email string `json:"email"` Errors []error `json:"errors"` } /* def __init__(self, id_=None, first=None, last=None, phone=None, email=None): */ func NewContact(args anyMap) *Contact { c := Contact{ Errors: make([]error, 0), } // Note: the "ok" are for the type assertions. The values are already set // to the 0V (or a slice for errors) at this point anyway. if id, ok := args["id"].(uint64); ok { c.ID = id } if first, ok := args["first"].(string); ok { c.First = first } if last, ok := args["last"].(string); ok { c.Last = last } if phone, ok := args["phone"].(string); ok { c.Phone = phone } if email, ok := args["email"].(string); ok { c.Email = email } return &c } /* def __str__(self): */ func (self *Contact) String() string { bs, err := json.Marshal(*self) if err != nil { log.Printf("Error marshaling contact: %v", err) } return string(bs) } /* def update(self, first, last, phone, email): */ func (self *Contact) Update(first, last, phone, email string) { self.First = first self.Last = last self.Phone = phone self.Email = email } /* def validate(self): */ func (self *Contact) validate() bool { if self.Email == "" { self.Errors = append(self.Errors, errors.New("Email required")) } for id, contact := range db { if id != self.ID && contact.Email == self.Email { self.Errors = append(self.Errors, errors.New("Email Must Be Unique")) break } } return len(self.Errors) == 0 } /* def save(self): */ func (self *Contact) Save() bool { if !self.validate() { return false } var maxID uint64 if self.ID == 0 { if len(db) == 0 { maxID = 1 } else { for id := range db { if id > maxID { maxID = id } } } self.ID = maxID + 1 } db[self.ID] = *self SaveDB() return true } /* def delete(self): */ func (self *Contact) Delete() { delete(db, self.ID) } /* @classmethod def count(cls): */ func (*Contact) Count() int { time.Sleep(2 * time.Second) return len(db) } /* @classmethod def all(cls, page=1): */ func (*Contact) All(page int) []Contact { if page == 0 { page = 1 } start := (page - 1) * PageSize end := min(start+PageSize, start+len(db)) ids := make([]uint64, 0, len(db)) for id := range db { ids = append(ids, id) } slices.SortStableFunc(ids, cmp.Compare[uint64]) pageIDs := ids[start:end] contacts := make([]Contact, 0, len(pageIDs)) for _, id := range pageIDs { contacts = append(contacts, db[id]) } return contacts } /* @classmethod def search(cls, text): */ func (*Contact) Search(text string) []Contact { var result []Contact for _, c := range db { matchFirst := c.First != "" && strings.Contains(c.First, text) matchLast := c.Last != "" && strings.Contains(c.Last, text) matchEmail := c.Email != "" && strings.Contains(c.Email, text) matchPhone := c.Phone != "" && strings.Contains(c.Phone, text) if matchFirst || matchLast || matchEmail || matchPhone { result = append(result, c) } } return result } /* @classmethod def load_db(cls): */ func (*Contact) LoadDB() { db = make(map[uint64]Contact, 0) contacts := make([]Contact, 0) bs, err := os.ReadFile((&Archiver{}).ArchiveFile()) if err != nil { panic(err) } if err := json.Unmarshal(bs, &contacts); err != nil { panic(err) } for _, c := range contacts { db[c.ID] = c } } /* @staticmethod def save_db(): */ func SaveDB() { contacts := make([]Contact, 0, len(db)) for _, contact := range db { contacts = append(contacts, contact) } slices.SortStableFunc(contacts, func(a, b Contact) int { return cmp.Compare(a.ID, b.ID) }) f, err := os.Create((&Archiver{}).ArchiveFile()) if err != nil { panic(err) } defer f.Close() enc := json.NewEncoder(f) if err := enc.Encode(contacts); err != nil { panic(err) } } /* @classmethod def find(cls, id_): */ func (*Contact) Find(id uint64) Contact { c, ok := db[id] // TODO shouldn't it be if !ok ? Original Python logic seems wrong. if ok { c.Errors = make([]error, 0) } return c } type Archiver struct { archiveStatus string archiveProgress float64 sync.RWMutex } const ( statusWaiting = "Waiting" statusRunning = "Running" statusComplete = "Complete" ) var ( archiver *Archiver ) /* def status(self): */ func (self *Archiver) Status() string { self.RLock() defer self.RUnlock() return self.archiveStatus } /* def progress(self): */ func (self *Archiver) Progress() float64 { self.RLock() defer self.RUnlock() return self.archiveProgress } /* def run(self): */ func (self *Archiver) Run() { self.Lock() defer self.Unlock() if self.archiveStatus == statusWaiting { self.archiveStatus = statusRunning self.archiveProgress = 0.0 go self.RunImpl() } } /* def run_impl(self): */ func (self *Archiver) RunImpl() { t0 := time.Now() for i := range 10 { time.Sleep(time.Duration(float64(time.Second) * rand.Float64())) self.Lock() if self.archiveStatus != statusRunning { self.Unlock() return } self.archiveProgress = float64(i+1) / 10 log.Printf("Here... after %v: %f", time.Since(t0), self.archiveProgress) self.Unlock() } time.Sleep(time.Second) self.Lock() if self.archiveStatus != statusRunning { self.Unlock() return } self.archiveStatus = statusComplete self.Unlock() log.Printf("RunImpl took %v", time.Since(t0)) } /* def archive_file(self): */ func (self *Archiver) ArchiveFile() string { return "contacts.json" } /* def reset(self): */ func (self *Archiver) Reset() { self.Lock() defer self.Unlock() self.archiveStatus = statusWaiting self.archiveProgress = 0.0 } /* @classmethod def get(cls): */ func GetArchiver() *Archiver { if archiver == nil { archiver = &Archiver{ archiveStatus: statusWaiting, } } return archiver }