goprotobuf: push out internal changes.
  - group text parsing and formatting
  - foreign extension types

R=adg
CC=golang-dev
http://codereview.appspot.com/4437079
diff --git a/proto/text.go b/proto/text.go
index 10f24b6..1ca46e8 100644
--- a/proto/text.go
+++ b/proto/text.go
@@ -32,8 +32,7 @@
 package proto
 
 // Functions for writing the Text protocol buffer format.
-// TODO:
-//	- Message sets, groups.
+// TODO: message sets, extensions.
 
 import (
 	"bytes"
@@ -51,6 +50,8 @@
 	complete     bool // if the current position is a complete line
 	compact      bool // whether to write out as a one-liner
 	writer       io.Writer
+
+	c [1]byte // scratch
 }
 
 func (w *textWriter) Write(p []byte) (n int, err os.Error) {
@@ -80,6 +81,12 @@
 	return
 }
 
+func (w *textWriter) WriteByte(c byte) os.Error {
+	w.c[0] = c
+	_, err := w.Write(w.c[:])
+	return err
+}
+
 func (w *textWriter) indent() { w.indent_level++ }
 
 func (w *textWriter) unindent() {
@@ -90,6 +97,13 @@
 	}
 }
 
+func writeName(w *textWriter, props *Properties) {
+	io.WriteString(w, props.OrigName)
+	if props.Wire != "group" {
+		w.WriteByte(':')
+	}
+}
+
 func writeStruct(w *textWriter, sv reflect.Value) {
 	st := sv.Type()
 	sprops := GetProperties(st)
@@ -114,23 +128,23 @@
 			if av := fv; av.Kind() == reflect.Slice {
 				// Repeated field.
 				for j := 0; j < av.Len(); j++ {
-					fmt.Fprintf(w, "%v:", props.OrigName)
+					writeName(w, props)
 					if !w.compact {
-						w.Write([]byte{' '})
+						w.WriteByte(' ')
 					}
-					writeAny(w, av.Index(j))
+					writeAny(w, av.Index(j), props)
 					fmt.Fprint(w, "\n")
 				}
 				continue
 			}
 		}
 
-		fmt.Fprintf(w, "%v:", props.OrigName)
+		writeName(w, props)
 		if !w.compact {
-			w.Write([]byte{' '})
+			w.WriteByte(' ')
 		}
 		if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
-			writeAny(w, fv)
+			writeAny(w, fv, props)
 		}
 		fmt.Fprint(w, "\n")
 	}
@@ -153,7 +167,7 @@
 	return true
 }
 
-func writeAny(w *textWriter, v reflect.Value) {
+func writeAny(w *textWriter, v reflect.Value, props *Properties) {
 	v = reflect.Indirect(v)
 
 	// We don't attempt to serialise every possible value type; only those
@@ -173,16 +187,18 @@
 		fmt.Fprint(w, strconv.Quote(val.String()))
 	case reflect.Struct:
 		// Required/optional group/message.
-		// TODO: groups use { } instead of < >, and no colon.
+		var bra, ket byte = '<', '>'
+		if props != nil && props.Wire == "group" {
+			bra, ket = '{', '}'
+		}
+		w.WriteByte(bra)
 		if !w.compact {
-			fmt.Fprint(w, "<\n")
-		} else {
-			fmt.Fprint(w, "<")
+			w.WriteByte('\n')
 		}
 		w.indent()
 		writeStruct(w, val)
 		w.unindent()
-		fmt.Fprint(w, ">")
+		w.WriteByte(ket)
 	default:
 		fmt.Fprint(w, val.Interface())
 	}
@@ -205,7 +221,7 @@
 	if v.Kind() == reflect.Struct {
 		writeStruct(aw, v)
 	} else {
-		writeAny(aw, v)
+		writeAny(aw, v, nil)
 	}
 }