| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 | package mainimport (	"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]}
 |