aboutsummaryrefslogtreecommitdiff
path: root/examples/add_person.go
diff options
context:
space:
mode:
Diffstat (limited to 'examples/add_person.go')
-rw-r--r--examples/add_person.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/examples/add_person.go b/examples/add_person.go
new file mode 100644
index 00000000..6b2d3d69
--- /dev/null
+++ b/examples/add_person.go
@@ -0,0 +1,128 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+ pb "github.com/google/protobuf/examples/tutorial"
+)
+
+func promptForAddress(r io.Reader) (*pb.Person, error) {
+ // A protocol buffer can be created like any struct.
+ p := &pb.Person{}
+
+ rd := bufio.NewReader(r)
+ fmt.Print("Enter person ID number: ")
+ // An int32 field in the .proto file is represented as an int32 field
+ // in the generated Go struct.
+ if _, err := fmt.Fscanf(rd, "%d\n", &p.Id); err != nil {
+ return p, err
+ }
+
+ fmt.Print("Enter name: ")
+ name, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ // A string field in the .proto file results in a string field in Go.
+ // We trim the whitespace because rd.ReadString includes the trailing
+ // newline character in its output.
+ p.Name = strings.TrimSpace(name)
+
+ fmt.Print("Enter email address (blank for none): ")
+ email, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ p.Email = strings.TrimSpace(email)
+
+ for {
+ fmt.Print("Enter a phone number (or leave blank to finish): ")
+ phone, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ phone = strings.TrimSpace(phone)
+ if phone == "" {
+ break
+ }
+ // The PhoneNumber message type is nested within the Person
+ // message in the .proto file. This results in a Go struct
+ // named using the name of the parent prefixed to the name of
+ // the nested message. Just as with pb.Person, it can be
+ // created like any other struct.
+ pn := &pb.Person_PhoneNumber{
+ Number: phone,
+ }
+
+ fmt.Print("Is this a mobile, home, or work phone? ")
+ ptype, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ ptype = strings.TrimSpace(ptype)
+
+ // A proto enum results in a Go constant for each enum value.
+ switch ptype {
+ case "mobile":
+ pn.Type = pb.Person_MOBILE
+ case "home":
+ pn.Type = pb.Person_HOME
+ case "work":
+ pn.Type = pb.Person_WORK
+ default:
+ fmt.Printf("Unknown phone type %q. Using default.\n", ptype)
+ }
+
+ // A repeated proto field maps to a slice field in Go. We can
+ // append to it like any other slice.
+ p.Phones = append(p.Phones, pn)
+ }
+
+ return p, nil
+}
+
+// Main reads the entire address book from a file, adds one person based on
+// user input, then writes it back out to the same file.
+func main() {
+ if len(os.Args) != 2 {
+ log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0])
+ }
+ fname := os.Args[1]
+
+ // Read the existing address book.
+ in, err := ioutil.ReadFile(fname)
+ if err != nil {
+ if os.IsNotExist(err) {
+ fmt.Printf("%s: File not found. Creating new file.\n", fname)
+ } else {
+ log.Fatalln("Error reading file:", err)
+ }
+ }
+ book := &pb.AddressBook{}
+ if err := proto.Unmarshal(in, book); err != nil {
+ log.Fatalln("Failed to parse address book:", err)
+ }
+
+ // Add an address.
+ addr, err := promptForAddress(os.Stdin)
+ if err != nil {
+ log.Fatalln("Error with address:", err)
+ }
+ book.People = append(book.People, addr)
+
+ // Write the new address book back to disk.
+ out, err := proto.Marshal(book)
+ if err != nil {
+ log.Fatalln("Failed to encode address book:", err)
+ }
+ if err := ioutil.WriteFile(fname, out, 0644); err != nil {
+ log.Fatalln("Failed to write address book:", err)
+ }
+}