encoding/protobuf: implement time type conversions

Adheres to protobuf JSON mapping.

Also fixes a bug where a filed could mask the short
name of the import time packag.

Change-Id: Ib5e71023a4d68492e35da02e997c47f4e09e3dd5
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2722
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index 7f36f7f..8280f06 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -63,12 +63,12 @@
 	tfile.SetLinesForContent(b)
 
 	p = &protoConverter{
-		id:      filename,
-		state:   s,
-		tfile:   tfile,
-		used:    map[string]bool{},
-		symbols: map[string]bool{},
-		aliases: map[string]string{},
+		id:       filename,
+		state:    s,
+		tfile:    tfile,
+		imported: map[string]bool{},
+		symbols:  map[string]bool{},
+		aliases:  map[string]string{},
 	}
 
 	defer func() {
@@ -133,14 +133,14 @@
 		p.topElement(e)
 	}
 
-	used := []string{}
-	for k := range p.used {
-		used = append(used, k)
+	imported := []string{}
+	for k := range p.imported {
+		imported = append(imported, k)
 	}
-	sort.Strings(used)
-	p.sorted = used
+	sort.Strings(imported)
+	p.sorted = imported
 
-	for _, v := range used {
+	for _, v := range imported {
 		spec := &ast.ImportSpec{
 			Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(v)},
 		}
@@ -172,8 +172,8 @@
 	file   *ast.File
 	inBody bool
 
-	sorted []string
-	used   map[string]bool
+	sorted   []string
+	imported map[string]bool
 
 	path    []string
 	scope   []map[string]mapping // for symbols resolution within package.
@@ -214,6 +214,15 @@
 		case *proto.Enum:
 			name = x.Name
 			pos = x.Position
+		case *proto.NormalField:
+			name = x.Name
+			pos = x.Position
+		case *proto.MapField:
+			name = x.Name
+			pos = x.Position
+		case *proto.Oneof:
+			name = x.Name
+			pos = x.Position
 		default:
 			continue
 		}
@@ -228,26 +237,25 @@
 }
 
 func (p *protoConverter) uniqueTop(name string) string {
-	if len(p.path) == 0 {
-		return name
-	}
 	a := strings.SplitN(name, ".", 2)
-	if p.path[len(p.path)-1] == a[0] {
-		first := a[0]
-		alias, ok := p.aliases[first]
-		if !ok {
-			// TODO: this is likely to be okay, but find something better.
-			alias = "__" + first
-			p.file.Decls = append(p.file.Decls, &ast.Alias{
-				Ident: ast.NewIdent(alias),
-				Expr:  ast.NewIdent(first),
-			})
-			p.aliases[first] = alias
+	for i := len(p.scope) - 1; i > 0; i-- {
+		if _, ok := p.scope[i][a[0]]; ok {
+			first := a[0]
+			alias, ok := p.aliases[first]
+			if !ok {
+				// TODO: this is likely to be okay, but find something better.
+				alias = "__" + first
+				p.file.Decls = append(p.file.Decls, &ast.Alias{
+					Ident: ast.NewIdent(alias),
+					Expr:  ast.NewIdent(first),
+				})
+				p.aliases[first] = alias
+			}
+			if len(a) > 1 {
+				alias += "." + a[1]
+			}
+			return alias
 		}
-		if len(a) > 1 {
-			alias += "." + a[1]
-		}
-		return alias
 	}
 	return name
 }
@@ -265,6 +273,9 @@
 }
 
 func (p *protoConverter) resolve(pos scanner.Position, name string, options []*proto.Option) string {
+	if s, ok := protoToCUE(name, options); ok {
+		return s
+	}
 	if strings.HasPrefix(name, ".") {
 		return p.resolveTopScope(pos, name[1:], options)
 	}
@@ -286,16 +297,13 @@
 		}
 		if m, ok := p.scope[0][name[:i]]; ok {
 			if m.pkg != nil {
-				p.used[m.pkg.goPkgPath] = true
+				p.imported[m.pkg.goPkgPath] = true
 				// TODO: do something more principled.
 			}
 			cueName := strings.Replace(name[i:], ".", "_", -1)
 			return p.uniqueTop(m.ref + cueName)
 		}
 	}
-	if s, ok := protoToCUE(name, options); ok {
-		return s
-	}
 	failf(pos, "name %q not found", name)
 	return ""
 }
diff --git a/encoding/protobuf/protobuf.go b/encoding/protobuf/protobuf.go
index 9ae5b64..a2a1413 100644
--- a/encoding/protobuf/protobuf.go
+++ b/encoding/protobuf/protobuf.go
@@ -21,29 +21,33 @@
 //
 // The following type mappings of defintions apply:
 //
-//   Proto type     CUE type/def   Comments
-//   message        struct         Message fields become CUE fields, whereby
-//                                 names are mapped to lowerCamelCase.
-//   enum           e1 | e2 | ...  Where ex are strings. A separate mapping is
-//                                 generated to obtain the numeric values.
-//   map<K, V>      { <>: V }      All keys are converted to strings.
-//   repeated V     [...V]         null is accepted as the empty list [].
+//   Proto type     CUE type/def     Comments
+//   message        struct           Message fields become CUE fields, whereby
+//                                   names are mapped to lowerCamelCase.
+//   enum           e1 | e2 | ...    Where ex are strings. A separate mapping is
+//                                   generated to obtain the numeric values.
+//   map<K, V>      { <>: V }        All keys are converted to strings.
+//   repeated V     [...V]           null is accepted as the empty list [].
 //   bool           bool
 //   string         string
-//   bytes          bytes          A base64-encoded string when converted to JSON.
-//   int32, fixed32 int32          An integer with bounds as defined by int32.
-//   uint32         uint32         An integer with bounds as defined by uint32.
-//   int64, fixed64 int64          An integer with bounds as defined by int64.
-//   uint64         uint64         An integer with bounds as defined by uint64.
-//   float          float32        A number with bounds as defined by float32.
-//   double         float64        A number with bounds as defined by float64.
-//   Struct         struct         See struct.proto.
-//   Value          _              See struct.proto.
-//   ListValue      [...]          See struct.proto.
-//   BoolValue      bool           See struct.proto.
-//   StringValue    string         See struct.proto.
-//   NumberValue    number         See struct.proto.
-//   StringValue    string         See struct.proto.
+//   bytes          bytes            A base64-encoded string when converted to JSON.
+//   int32, fixed32 int32            An integer with bounds as defined by int32.
+//   uint32         uint32           An integer with bounds as defined by uint32.
+//   int64, fixed64 int64            An integer with bounds as defined by int64.
+//   uint64         uint64           An integer with bounds as defined by uint64.
+//   float          float32          A number with bounds as defined by float32.
+//   double         float64          A number with bounds as defined by float64.
+//   Struct         struct           See struct.proto.
+//   Value          _                See struct.proto.
+//   ListValue      [...]            See struct.proto.
+//   NullValue      null             See struct.proto.
+//   BoolValue      bool             See struct.proto.
+//   StringValue    string           See struct.proto.
+//   NumberValue    number           See struct.proto.
+//   StringValue    string           See struct.proto.
+//   Empty          struct.MaxFields(0)
+//   Timestamp      time.Time        See struct.proto.
+//   Duration       time.Duration    See struct.proto.
 //
 // Protobuf definitions can be annotated with CUE constraints that are
 // included in the generated CUE:
@@ -58,12 +62,12 @@
 package protobuf
 
 // TODO mappings:
-// Timestamp	string	"1972-01-01T10:00:20.021Z"	Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than "Z" are also accepted.
-// Duration	string	"1.000340012s", "1s"	Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix "s". Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix "s" is required.
-// Empty	object	{}
 //
 // Wrapper types	various types	2, "2", "foo", true, "true", null, 0, …	Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.
 // FieldMask	string	"f.fooBar,h"	See field_mask.proto.
+//   Any            {"@type":"url",  See struct.proto.
+//                   f1: value,
+//                   ...}
 
 import (
 	"os"
@@ -238,9 +242,7 @@
 		}
 		f, err = parser.ParseFile(f.Filename, buf, parser.ParseComments)
 		if err != nil {
-			panic(err)
 			b.addErr(err)
-			// return nil, err
 			continue
 		}
 
@@ -251,7 +253,7 @@
 		// 	return nil, err
 		// }
 
-		for pkg := range r.p.used {
+		for pkg := range r.p.imported {
 			inst.ImportPaths = append(inst.ImportPaths, pkg)
 		}
 	}
diff --git a/encoding/protobuf/protobuf_test.go b/encoding/protobuf/protobuf_test.go
index d62951d..bd5a905 100644
--- a/encoding/protobuf/protobuf_test.go
+++ b/encoding/protobuf/protobuf_test.go
@@ -120,7 +120,7 @@
 		gotFiles[rel] = f
 	}
 
-	filepath.Walk("testdata/istio.io/api", func(path string, fi os.FileInfo, err error) error {
+	_ = filepath.Walk("testdata/istio.io/api", func(path string, fi os.FileInfo, err error) error {
 		if err != nil || fi.IsDir() || !strings.HasSuffix(path, ".cue") {
 			return err
 		}
diff --git a/encoding/protobuf/testdata/attributes.proto.out.cue b/encoding/protobuf/testdata/attributes.proto.out.cue
index 055c6c8..73f2cd8 100644
--- a/encoding/protobuf/testdata/attributes.proto.out.cue
+++ b/encoding/protobuf/testdata/attributes.proto.out.cue
@@ -14,10 +14,7 @@
 //  limitations under the License.
 package v1
 
-import (
-	"github.com/golang/protobuf/ptypes/duration"
-	"github.com/golang/protobuf/ptypes/timestamp"
-)
+import "time"
 
 StructWrap: {
 	struct?:    {}     @protobuf(1,type=google.protobuf.Struct)
@@ -91,10 +88,10 @@
 	bytesValue?: bytes @protobuf(6,name=bytes_value)
 } | {
 	//  Used for values of type TIMESTAMP
-	timestampValue?: timestamp.Timestamp @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+	timestampValue?: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
 } | {
 	//  Used for values of type DURATION
-	durationValue?: duration.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
+	durationValue?: time.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
 } | {
 	//  Used for values of type STRING_MAP
 	stringMapValue?: Attributes_StringMap @protobuf(9,type=StringMap,name=string_map_value)
@@ -140,13 +137,13 @@
 	} @protobuf(5,type=map<sint32,bool>)
 
 	//  Holds attributes of type TIMESTAMP
-	timestamps: {
-		<_>: timestamp.Timestamp
+	time: {
+		<_>: __time.Time
 	} @protobuf(6,type=map<sint32,google.protobuf.Timestamp>,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
 
 	//  Holds attributes of type DURATION
 	durations: {
-		<_>: duration.Duration
+		<_>: __time.Duration
 	} @protobuf(7,type=map<sint32,google.protobuf.Duration>,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	//  Holds attributes of type BYTES
@@ -159,6 +156,7 @@
 		<_>: StringMap
 	} @protobuf(9,type=map<sint32,StringMap>,string_maps,"(gogoproto.nullable)=false")
 }
+__time = time
 
 //  A map of string to string. The keys and values in this map are dictionary
 //  indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
diff --git a/encoding/protobuf/testdata/client_config.proto.out.cue b/encoding/protobuf/testdata/client_config.proto.out.cue
index a3db5df..f8535a7 100644
--- a/encoding/protobuf/testdata/client_config.proto.out.cue
+++ b/encoding/protobuf/testdata/client_config.proto.out.cue
@@ -21,8 +21,8 @@
 package client
 
 import (
-	"github.com/golang/protobuf/ptypes/duration"
 	"istio.io/api/mixer/v1"
+	"time"
 )
 
 //  Specifies the behavior when the client is unable to connect to Mixer.
@@ -36,10 +36,10 @@
 
 	//  Base time to wait between retries.  Will be adjusted by exponential
 	//  backoff and jitter.
-	baseRetryWait?: duration.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
+	baseRetryWait?: time.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
 
 	//  Max time to wait between retries.
-	maxRetryWait?: duration.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
+	maxRetryWait?: time.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
 }
 
 //  Example of single-value enum.
@@ -108,7 +108,7 @@
 
 	//  Specify refresh interval to write Mixer client statistics to Envoy share
 	//  memory. If not specified, the interval is 10 seconds.
-	statsUpdateInterval?: duration.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
+	statsUpdateInterval?: time.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
 
 	//  Name of the cluster that will forward check calls to a pool of mixer
 	//  servers. Defaults to "mixer_server". By using different names for
@@ -185,5 +185,5 @@
 	//  Specify report interval to send periodical reports for long TCP
 	//  connections. If not specified, the interval is 10 seconds. This interval
 	//  should not be less than 1 second, otherwise it will be reset to 1 second.
-	reportInterval?: duration.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
+	reportInterval?: time.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
index 780005c..ddc1b94 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
@@ -139,7 +139,7 @@
   map<sint32, bool> bools = 5;
 
   // Holds attributes of type TIMESTAMP
-  map<sint32, google.protobuf.Timestamp> timestamps = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
+  map<sint32, google.protobuf.Timestamp> time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
 
   // Holds attributes of type DURATION
   map<sint32, google.protobuf.Duration> durations = 7 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
index e5ee16d..085190a 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
@@ -14,10 +14,7 @@
 //  limitations under the License.
 package v1
 
-import (
-	"github.com/golang/protobuf/ptypes/duration"
-	"github.com/golang/protobuf/ptypes/timestamp"
-)
+import "time"
 
 StructWrap: {
 	struct?: {} @protobuf(1,type=google.protobuf.Struct)
@@ -91,10 +88,10 @@
 	bytesValue?: bytes @protobuf(6,name=bytes_value)
 } | {
 	//  Used for values of type TIMESTAMP
-	timestampValue?: timestamp.Timestamp @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+	timestampValue?: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
 } | {
 	//  Used for values of type DURATION
-	durationValue?: duration.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
+	durationValue?: time.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
 } | {
 	//  Used for values of type STRING_MAP
 	stringMapValue?: Attributes_StringMap @protobuf(9,type=StringMap,name=string_map_value)
@@ -140,13 +137,13 @@
 	} @protobuf(5,type=map<sint32,bool>)
 
 	//  Holds attributes of type TIMESTAMP
-	timestamps: {
-		<_>: timestamp.Timestamp
+	time: {
+		<_>: __time.Time
 	} @protobuf(6,type=map<sint32,google.protobuf.Timestamp>,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
 
 	//  Holds attributes of type DURATION
 	durations: {
-		<_>: duration.Duration
+		<_>: __time.Duration
 	} @protobuf(7,type=map<sint32,google.protobuf.Duration>,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	//  Holds attributes of type BYTES
@@ -159,6 +156,7 @@
 		<_>: StringMap
 	} @protobuf(9,type=map<sint32,StringMap>,string_maps,"(gogoproto.nullable)=false")
 }
+__time = time
 
 //  A map of string to string. The keys and values in this map are dictionary
 //  indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
index 771cc01..dd44e5c 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
@@ -20,8 +20,8 @@
 package client
 
 import (
-	"github.com/golang/protobuf/ptypes/duration"
 	"istio.io/api/mixer/v1"
+	"time"
 )
 
 //  Specifies the behavior when the client is unable to connect to Mixer.
@@ -35,10 +35,10 @@
 
 	//  Base time to wait between retries.  Will be adjusted by exponential
 	//  backoff and jitter.
-	baseRetryWait?: duration.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
+	baseRetryWait?: time.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
 
 	//  Max time to wait between retries.
-	maxRetryWait?: duration.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
+	maxRetryWait?: time.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
 }
 
 //  Example of single-value enum.
@@ -107,7 +107,7 @@
 
 	//  Specify refresh interval to write Mixer client statistics to Envoy share
 	//  memory. If not specified, the interval is 10 seconds.
-	statsUpdateInterval?: duration.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
+	statsUpdateInterval?: time.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
 
 	//  Name of the cluster that will forward check calls to a pool of mixer
 	//  servers. Defaults to "mixer_server". By using different names for
@@ -184,5 +184,5 @@
 	//  Specify report interval to send periodical reports for long TCP
 	//  connections. If not specified, the interval is 10 seconds. This interval
 	//  should not be less than 1 second, otherwise it will be reset to 1 second.
-	reportInterval?: duration.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
+	reportInterval?: time.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
index 44a4cad..c369365 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
@@ -17,8 +17,8 @@
 package v1
 
 import (
-	"github.com/golang/protobuf/ptypes/duration"
 	"google.golang.org/genproto/googleapis/rpc/status"
+	"time"
 )
 
 //  Used to get a thumbs-up/thumbs-down before performing an action.
@@ -74,7 +74,7 @@
 	status?: __status.Status @protobuf(1,type=google.rpc.Status,"(gogoproto.nullable)=false")
 
 	//  The amount of time for which this result can be considered valid.
-	validDuration?: duration.Duration @protobuf(2,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+	validDuration?: time.Duration @protobuf(2,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	//  The number of uses for which this result can be considered valid.
 	validUseCount?: int32 @protobuf(3,name=valid_use_count)
@@ -92,7 +92,7 @@
 //  Expresses the result of a quota allocation.
 CheckResponse_QuotaResult: {
 	//  The amount of time for which this result can be considered valid.
-	validDuration?: duration.Duration @protobuf(1,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+	validDuration?: time.Duration @protobuf(1,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	//  The amount of granted quota. When `QuotaParams.best_effort` is true, this will be >= 0.
 	//  If `QuotaParams.best_effort` is false, this will be either 0 or >= `QuotaParams.amount`.
diff --git a/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/duration/duration_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/duration/duration_proto_gen.cue
deleted file mode 100644
index bca02c4..0000000
--- a/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/duration/duration_proto_gen.cue
+++ /dev/null
@@ -1,106 +0,0 @@
-
-//  Protocol Buffers - Google's data interchange format
-//  Copyright 2008 Google Inc.  All rights reserved.
-//  https://developers.google.com/protocol-buffers/
-// 
-//  Redistribution and use in source and binary forms, with or without
-//  modification, are permitted provided that the following conditions are
-//  met:
-// 
-//      * Redistributions of source code must retain the above copyright
-//  notice, this list of conditions and the following disclaimer.
-//      * Redistributions in binary form must reproduce the above
-//  copyright notice, this list of conditions and the following disclaimer
-//  in the documentation and/or other materials provided with the
-//  distribution.
-//      * Neither the name of Google Inc. nor the names of its
-//  contributors may be used to endorse or promote products derived from
-//  this software without specific prior written permission.
-// 
-//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-package duration
-
-//  A Duration represents a signed, fixed-length span of time represented
-//  as a count of seconds and fractions of seconds at nanosecond
-//  resolution. It is independent of any calendar and concepts like "day"
-//  or "month". It is related to Timestamp in that the difference between
-//  two Timestamp values is a Duration and it can be added or subtracted
-//  from a Timestamp. Range is approximately +-10,000 years.
-// 
-//  # Examples
-// 
-//  Example 1: Compute Duration from two Timestamps in pseudo code.
-// 
-//      Timestamp start = ...;
-//      Timestamp end = ...;
-//      Duration duration = ...;
-// 
-//      duration.seconds = end.seconds - start.seconds;
-//      duration.nanos = end.nanos - start.nanos;
-// 
-//      if (duration.seconds < 0 && duration.nanos > 0) {
-//        duration.seconds += 1;
-//        duration.nanos -= 1000000000;
-//      } else if (durations.seconds > 0 && duration.nanos < 0) {
-//        duration.seconds -= 1;
-//        duration.nanos += 1000000000;
-//      }
-// 
-//  Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
-// 
-//      Timestamp start = ...;
-//      Duration duration = ...;
-//      Timestamp end = ...;
-// 
-//      end.seconds = start.seconds + duration.seconds;
-//      end.nanos = start.nanos + duration.nanos;
-// 
-//      if (end.nanos < 0) {
-//        end.seconds -= 1;
-//        end.nanos += 1000000000;
-//      } else if (end.nanos >= 1000000000) {
-//        end.seconds += 1;
-//        end.nanos -= 1000000000;
-//      }
-// 
-//  Example 3: Compute Duration from datetime.timedelta in Python.
-// 
-//      td = datetime.timedelta(days=3, minutes=10)
-//      duration = Duration()
-//      duration.FromTimedelta(td)
-// 
-//  # JSON Mapping
-// 
-//  In JSON format, the Duration type is encoded as a string rather than an
-//  object, where the string ends in the suffix "s" (indicating seconds) and
-//  is preceded by the number of seconds, with nanoseconds expressed as
-//  fractional seconds. For example, 3 seconds with 0 nanoseconds should be
-//  encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
-//  be expressed in JSON format as "3.000000001s", and 3 seconds and 1
-//  microsecond should be expressed in JSON format as "3.000001s".
-// 
-// 
-Duration: {
-	//  Signed seconds of the span of time. Must be from -315,576,000,000
-	//  to +315,576,000,000 inclusive. Note: these bounds are computed from:
-	//  60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
-	seconds?: int64 @protobuf(1)
-
-	//  Signed fractions of a second at nanosecond resolution of the span
-	//  of time. Durations less than one second are represented with a 0
-	//  `seconds` field and a positive or negative `nanos` field. For durations
-	//  of one second or more, a non-zero value for the `nanos` field must be
-	//  of the same sign as the `seconds` field. Must be from -999,999,999
-	//  to +999,999,999 inclusive.
-	nanos?: int32 @protobuf(2)
-}
diff --git a/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/timestamp/timestamp_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/timestamp/timestamp_proto_gen.cue
deleted file mode 100644
index f11f6e3..0000000
--- a/encoding/protobuf/testdata/istio.io/api/pkg/github.com/golang/protobuf/ptypes/timestamp/timestamp_proto_gen.cue
+++ /dev/null
@@ -1,128 +0,0 @@
-
-//  Protocol Buffers - Google's data interchange format
-//  Copyright 2008 Google Inc.  All rights reserved.
-//  https://developers.google.com/protocol-buffers/
-// 
-//  Redistribution and use in source and binary forms, with or without
-//  modification, are permitted provided that the following conditions are
-//  met:
-// 
-//      * Redistributions of source code must retain the above copyright
-//  notice, this list of conditions and the following disclaimer.
-//      * Redistributions in binary form must reproduce the above
-//  copyright notice, this list of conditions and the following disclaimer
-//  in the documentation and/or other materials provided with the
-//  distribution.
-//      * Neither the name of Google Inc. nor the names of its
-//  contributors may be used to endorse or promote products derived from
-//  this software without specific prior written permission.
-// 
-//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-package timestamp
-
-//  A Timestamp represents a point in time independent of any time zone or local
-//  calendar, encoded as a count of seconds and fractions of seconds at
-//  nanosecond resolution. The count is relative to an epoch at UTC midnight on
-//  January 1, 1970, in the proleptic Gregorian calendar which extends the
-//  Gregorian calendar backwards to year one.
-// 
-//  All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
-//  second table is needed for interpretation, using a [24-hour linear
-//  smear](https://developers.google.com/time/smear).
-// 
-//  The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
-//  restricting to that range, we ensure that we can convert to and from [RFC
-//  3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
-// 
-//  # Examples
-// 
-//  Example 1: Compute Timestamp from POSIX `time()`.
-// 
-//      Timestamp timestamp;
-//      timestamp.set_seconds(time(NULL));
-//      timestamp.set_nanos(0);
-// 
-//  Example 2: Compute Timestamp from POSIX `gettimeofday()`.
-// 
-//      struct timeval tv;
-//      gettimeofday(&tv, NULL);
-// 
-//      Timestamp timestamp;
-//      timestamp.set_seconds(tv.tv_sec);
-//      timestamp.set_nanos(tv.tv_usec * 1000);
-// 
-//  Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
-// 
-//      FILETIME ft;
-//      GetSystemTimeAsFileTime(&ft);
-//      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
-// 
-//      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
-//      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
-//      Timestamp timestamp;
-//      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
-//      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
-// 
-//  Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
-// 
-//      long millis = System.currentTimeMillis();
-// 
-//      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
-//          .setNanos((int) ((millis % 1000) * 1000000)).build();
-// 
-// 
-//  Example 5: Compute Timestamp from current time in Python.
-// 
-//      timestamp = Timestamp()
-//      timestamp.GetCurrentTime()
-// 
-//  # JSON Mapping
-// 
-//  In JSON format, the Timestamp type is encoded as a string in the
-//  [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
-//  format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
-//  where {year} is always expressed using four digits while {month}, {day},
-//  {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
-//  seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
-//  are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
-//  is required. A proto3 JSON serializer should always use UTC (as indicated by
-//  "Z") when printing the Timestamp type and a proto3 JSON parser should be
-//  able to accept both UTC and other timezones (as indicated by an offset).
-// 
-//  For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
-//  01:30 UTC on January 15, 2017.
-// 
-//  In JavaScript, one can convert a Date object to this format using the
-//  standard
-//  [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
-//  method. In Python, a standard `datetime.datetime` object can be converted
-//  to this format using
-//  [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
-//  the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
-//  the Joda Time's [`ISODateTimeFormat.dateTime()`](
-//  http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
-//  ) to obtain a formatter capable of generating timestamps in this format.
-// 
-// 
-Timestamp: {
-	//  Represents seconds of UTC time since Unix epoch
-	//  1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
-	//  9999-12-31T23:59:59Z inclusive.
-	seconds?: int64 @protobuf(1)
-
-	//  Non-negative fractions of a second at nanosecond resolution. Negative
-	//  second values with fractions must still have non-negative nanos values
-	//  that count forward in time. Must be from 0 to 999,999,999
-	//  inclusive.
-	nanos?: int32 @protobuf(2)
-}
diff --git a/encoding/protobuf/types.go b/encoding/protobuf/types.go
index f54708e..5fff043 100644
--- a/encoding/protobuf/types.go
+++ b/encoding/protobuf/types.go
@@ -52,6 +52,11 @@
 	p.scope[0][from] = mapping{to, "", pkg}
 }
 
+var (
+	pkgTime   = &protoConverter{goPkgPath: "time"}
+	pkgStruct = &protoConverter{goPkgPath: "struct"}
+)
+
 func (p *protoConverter) mapBuiltinPackage(pos scanner.Position, file string, required bool) (generate bool) {
 	// Map some builtin types to their JSON/CUE mappings.
 	switch file {
@@ -67,16 +72,22 @@
 		p.setBuiltin("google.protobuf.NumberValue", "number", nil)
 		return false
 
-	// TODO: consider mapping the following:
+	case "google/protobuf/empty.proto":
+		p.setBuiltin("google.protobuf.Empty", "struct.MaxFields(0)", pkgStruct)
+		return false
 
-	// case "google/protobuf/duration.proto":
-	// 	p.setBuiltin("google.protobuf.Duration", "time.Duration", "time")
+	case "google/protobuf/duration.proto":
+		p.setBuiltin("google.protobuf.Duration", "time.Duration", pkgTime)
+		return false
 
-	// case "google/protobuf/timestamp.proto":
-	// 	p.setBuiltin("google.protobuf.Timestamp", "time.Time", "time")
+	case "google/protobuf/timestamp.proto":
+		p.setBuiltin("google.protobuf.Timestamp", "time.Time", pkgTime)
+		return false
 
-	// case "google/protobuf/empty.proto":
-	// 	p.setBuiltin("google.protobuf.Empty", "struct.MaxFields(0)", nil)
+	// case "google/protobuf/field_mask.proto":
+	// 	p.setBuiltin("google.protobuf.FieldMask", "protobuf.FieldMask", nil)
+
+	// 	protobuf.Any
 
 	default:
 		if required {