|  | 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) | 
|  | } | 
|  | } | 
|  |  | 
|  | // [START marshal_proto] | 
|  | book := &pb.AddressBook{} | 
|  | // [START_EXCLUDE] | 
|  | 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) | 
|  | // [END_EXCLUDE] | 
|  |  | 
|  | // 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) | 
|  | } | 
|  | // [END marshal_proto] | 
|  | } |