internal/protobuf: add proto definition extraction

Change-Id: Ia356bcb951e30bcc8030b51cbc5d9949391e84dc
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1980
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/internal/protobuf/protobuf.go b/internal/protobuf/protobuf.go
new file mode 100644
index 0000000..4280d9c
--- /dev/null
+++ b/internal/protobuf/protobuf.go
@@ -0,0 +1,88 @@
+// Copyright 2019 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package protobuf defines functionality for parsing protocol buffer
+// definitions and instances.
+//
+// TODO: this package can become public once we have found a good nest for it.
+package protobuf
+
+import (
+	"fmt"
+	"io"
+
+	"cuelang.org/go/cue/ast"
+)
+
+// Config specifies the environment into which to parse a proto definition file.
+type Config struct {
+	Paths []string
+}
+
+// Parse parses a single proto file and returns its contents translated to
+// a CUE file. Imports are resolved using the path define in Config.
+// If body is not nil, it will use this as the contents of the file. Otherwise
+// Parse will open the given file name at the fully qualified path.
+//
+// The following field options are supported:
+//    (cue.val)     string        CUE constraint for this field. The string may
+//                                refer to other fields in a message definition.
+//    (cue.opt)     FieldOptions
+//       required   bool          Defines the field is required. Use with
+//                                caution.
+func Parse(filename string, body io.Reader, c *Config) (f *ast.File, err error) {
+	state := &sharedState{
+		paths: c.Paths,
+	}
+	p, err := state.parse(filename, body)
+	if err != nil {
+		return nil, err
+	}
+	return p.file, nil
+}
+
+// ProtoError describes the location and cause of an error.
+type ProtoError struct {
+	Filename string
+	Path     string
+	Err      error
+}
+
+func (p *ProtoError) Unwrap() error { return p.Err }
+
+func (p *ProtoError) Error() string {
+	if p.Path == "" {
+		return fmt.Sprintf("parse of file %q failed: %v", p.Filename, p.Err)
+	}
+	return fmt.Sprintf("parse of file %q failed at %s: %v", p.Filename, p.Path, p.Err)
+}
+
+// TODO
+// func GenDefinition
+
+// func MarshalText(cue.Value) (string, error) {
+// 	return "", nil
+// }
+
+// func MarshalBytes(cue.Value) ([]byte, error) {
+// 	return nil, nil
+// }
+
+// func UnmarshalText(descriptor cue.Value, b string) (ast.Expr, error) {
+// 	return nil, nil
+// }
+
+// func UnmarshalBytes(descriptor cue.Value, b []byte) (ast.Expr, error) {
+// 	return nil, nil
+// }