123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- 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
- }
|