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