diff --git a/lesson_4/phonebook/book/book.go b/lesson_4/phonebook/book/book.go new file mode 100644 index 0000000..277f4b9 --- /dev/null +++ b/lesson_4/phonebook/book/book.go @@ -0,0 +1,57 @@ +package book + +import ( + "fmt" + "time" +) + +type PhoneBook map[string]PhoneNumber + +type PhoneNumber struct { + Number string + LastUpdatedAt int64 +} + +func (book *PhoneBook) Add(name, phoneNum string) error { + if _, exists := (*book)[name]; exists { + return fmt.Errorf("name %s already exists", name) + } + + (*book)[name] = PhoneNumber{ + Number: phoneNum, + LastUpdatedAt: time.Now().Unix(), + } + + return nil +} + +func (book *PhoneBook) Get(name string) (PhoneNumber, error) { + if numberData, exists := (*book)[name]; exists { + return numberData, nil + } + + return PhoneNumber{}, fmt.Errorf("no entry found for %s", name) +} + +func (book *PhoneBook) Update(name, newPhoneNum string) error { + if _, exists := (*book)[name]; !exists { + return fmt.Errorf("name %s does not exist", name) + } + + (*book)[name] = PhoneNumber{ + Number: newPhoneNum, + LastUpdatedAt: time.Now().Unix(), + } + + return nil +} + +func (book *PhoneBook) Delete(name string) error { + if _, exists := (*book)[name]; !exists { + return fmt.Errorf("name %s does not exist", name) + } + + delete(*book, name) + + return nil +} diff --git a/lesson_4/phonebook/go.mod b/lesson_4/phonebook/go.mod new file mode 100644 index 0000000..000f3fe --- /dev/null +++ b/lesson_4/phonebook/go.mod @@ -0,0 +1,3 @@ +module phonebook + +go 1.22.2 diff --git a/lesson_4/phonebook/logger/logger.go b/lesson_4/phonebook/logger/logger.go new file mode 100644 index 0000000..b82cfbf --- /dev/null +++ b/lesson_4/phonebook/logger/logger.go @@ -0,0 +1,30 @@ +package logger + +import ( + "fmt" + "log" + "os" + "strings" +) + +var ( + warnLogger = log.New(os.Stdout, "WARN: ", log.LstdFlags) + infoLogger = log.New(os.Stdout, "INFO: ", log.LstdFlags) +) + +func Info(message string) { + infoLogger.Println(message) +} + +func Warn(err error, messages ...string) { + if err == nil { + return + } + + message := "An error occurred" + if len(messages) > 0 { + message = fmt.Sprintf("%s: %s", message, strings.Join(messages, " ")) + } + + warnLogger.Printf("%s: %v", message, err) +} diff --git a/lesson_4/phonebook/main.go b/lesson_4/phonebook/main.go new file mode 100644 index 0000000..57a38c8 --- /dev/null +++ b/lesson_4/phonebook/main.go @@ -0,0 +1,157 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "os" + "strings" + "time" + + "phonebook/book" + "phonebook/logger" +) + +func main() { + phoneBook := make(book.PhoneBook) + + scanner := bufio.NewScanner(os.Stdin) + fmt.Println("Welcome to the Phonebook") + fmt.Println("Available commands: add, get, delete, update, list, exit") + + for { + fmt.Print("> ") + if !scanner.Scan() { + break + } + line := scanner.Text() + parts := strings.SplitN(line, " ", 2) + command := parts[0] + args := parts[1:] + // add alice=12345 + switch command { + case "add": + handleCommand(doAdd, args, phoneBook) + case "get": + handleCommand(doGet, args, phoneBook) + case "list": + handleCommand(doList, args, phoneBook) + case "update": + handleCommand(doUpdate, args, phoneBook) + case "delete": + handleCommand(doDelete, args, phoneBook) + case "exit": + logger.Info("exiting phonebook, bye!") + return + default: + logger.Warn(errors.New("unsupported command. Try 'add', 'get', 'delete', 'update', 'list', or 'exit'")) + } + } +} + +func handleCommand(cmd func([]string, book.PhoneBook) error, args []string, phoneBook book.PhoneBook) { + if err := cmd(args, phoneBook); err != nil { + logger.Warn(err, "command failed") + } +} + +func doDelete(args []string, phoneBook book.PhoneBook) error { + if len(args) < 1 { + return errors.New("missing parameters for 'delete' command. Use: delete name") + } + + name := args[0] + + err := phoneBook.Delete(name) + if err != nil { + return err + } + + logger.Info(fmt.Sprintf("Deleted entry for %s\n", name)) + + return nil +} + +func doUpdate(args []string, phoneBook book.PhoneBook) error { + if len(args) < 1 { + return errors.New("missing parameters for 'update' command. Use: update name=new_number") + } + + kv := strings.SplitN(args[0], "=", 2) + if len(kv) != 2 { + return errors.New("invalid format. Use: update name=number") + } + + name, newNumber := kv[0], kv[1] + + err := phoneBook.Update(name, newNumber) + if err != nil { + return err + } + + logger.Info(fmt.Sprintf("Updated an entry: %s -> %s\n", name, newNumber)) + + return nil +} + +func doList(_ []string, phoneBook book.PhoneBook) error { + if len(phoneBook) == 0 { + return errors.New("phonebook is empty") + } else { + results := "" + + for name, number := range phoneBook { + results += fmt.Sprintf("%s -> %s\n", name, number.Number) + } + + logger.Info(results) + } + + return nil +} + +func doGet(args []string, phoneBook book.PhoneBook) error { + if len(args) < 1 { + return errors.New("missing parameters for 'get' command. Use: get name") + } + + name := args[0] + + numberData, err := phoneBook.Get(name) + if err != nil { + return err + } + + unixUpdatedAt := time.Unix(numberData.LastUpdatedAt, 0) + + logger.Info( + fmt.Sprintf("Number for %s is %s (last updated at %s)\n", + name, + numberData.Number, + unixUpdatedAt.Format("2006-01-02 15:04:05"), + ), + ) + + return nil +} + +func doAdd(args []string, phoneBook book.PhoneBook) error { + if len(args) < 1 { + return errors.New("missing parameters for 'add' command. Use: add name=number") + } + + kv := strings.SplitN(args[0], "=", 2) + if len(kv) != 2 { + return errors.New("invalid format. Use: add name=number") + } + + name, number := kv[0], kv[1] + err := phoneBook.Add(name, number) + if err != nil { + return err + } + + logger.Info(fmt.Sprintf("Added an entry: %s -> %s\n", name, number)) + + return nil +}