add_person.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "strings"
  10. "github.com/golang/protobuf/proto"
  11. pb "github.com/protocolbuffers/protobuf/examples/tutorial"
  12. )
  13. func promptForAddress(r io.Reader) (*pb.Person, error) {
  14. // A protocol buffer can be created like any struct.
  15. p := &pb.Person{}
  16. rd := bufio.NewReader(r)
  17. fmt.Print("Enter person ID number: ")
  18. // An int32 field in the .proto file is represented as an int32 field
  19. // in the generated Go struct.
  20. if _, err := fmt.Fscanf(rd, "%d\n", &p.Id); err != nil {
  21. return p, err
  22. }
  23. fmt.Print("Enter name: ")
  24. name, err := rd.ReadString('\n')
  25. if err != nil {
  26. return p, err
  27. }
  28. // A string field in the .proto file results in a string field in Go.
  29. // We trim the whitespace because rd.ReadString includes the trailing
  30. // newline character in its output.
  31. p.Name = strings.TrimSpace(name)
  32. fmt.Print("Enter email address (blank for none): ")
  33. email, err := rd.ReadString('\n')
  34. if err != nil {
  35. return p, err
  36. }
  37. p.Email = strings.TrimSpace(email)
  38. for {
  39. fmt.Print("Enter a phone number (or leave blank to finish): ")
  40. phone, err := rd.ReadString('\n')
  41. if err != nil {
  42. return p, err
  43. }
  44. phone = strings.TrimSpace(phone)
  45. if phone == "" {
  46. break
  47. }
  48. // The PhoneNumber message type is nested within the Person
  49. // message in the .proto file. This results in a Go struct
  50. // named using the name of the parent prefixed to the name of
  51. // the nested message. Just as with pb.Person, it can be
  52. // created like any other struct.
  53. pn := &pb.Person_PhoneNumber{
  54. Number: phone,
  55. }
  56. fmt.Print("Is this a mobile, home, or work phone? ")
  57. ptype, err := rd.ReadString('\n')
  58. if err != nil {
  59. return p, err
  60. }
  61. ptype = strings.TrimSpace(ptype)
  62. // A proto enum results in a Go constant for each enum value.
  63. switch ptype {
  64. case "mobile":
  65. pn.Type = pb.Person_MOBILE
  66. case "home":
  67. pn.Type = pb.Person_HOME
  68. case "work":
  69. pn.Type = pb.Person_WORK
  70. default:
  71. fmt.Printf("Unknown phone type %q. Using default.\n", ptype)
  72. }
  73. // A repeated proto field maps to a slice field in Go. We can
  74. // append to it like any other slice.
  75. p.Phones = append(p.Phones, pn)
  76. }
  77. return p, nil
  78. }
  79. // Main reads the entire address book from a file, adds one person based on
  80. // user input, then writes it back out to the same file.
  81. func main() {
  82. if len(os.Args) != 2 {
  83. log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0])
  84. }
  85. fname := os.Args[1]
  86. // Read the existing address book.
  87. in, err := ioutil.ReadFile(fname)
  88. if err != nil {
  89. if os.IsNotExist(err) {
  90. fmt.Printf("%s: File not found. Creating new file.\n", fname)
  91. } else {
  92. log.Fatalln("Error reading file:", err)
  93. }
  94. }
  95. // [START marshal_proto]
  96. book := &pb.AddressBook{}
  97. // [START_EXCLUDE]
  98. if err := proto.Unmarshal(in, book); err != nil {
  99. log.Fatalln("Failed to parse address book:", err)
  100. }
  101. // Add an address.
  102. addr, err := promptForAddress(os.Stdin)
  103. if err != nil {
  104. log.Fatalln("Error with address:", err)
  105. }
  106. book.People = append(book.People, addr)
  107. // [END_EXCLUDE]
  108. // Write the new address book back to disk.
  109. out, err := proto.Marshal(book)
  110. if err != nil {
  111. log.Fatalln("Failed to encode address book:", err)
  112. }
  113. if err := ioutil.WriteFile(fname, out, 0644); err != nil {
  114. log.Fatalln("Failed to write address book:", err)
  115. }
  116. // [END marshal_proto]
  117. }