| package main | 
 |  | 
 | import ( | 
 | 	"bufio" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"io/ioutil" | 
 | 	"log" | 
 | 	"os" | 
 | 	"strings" | 
 |  | 
 | 	"github.com/golang/protobuf/proto" | 
 | 	pb "github.com/protocolbuffers/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] | 
 | } |