// 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 import ( "fmt" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" "github.com/emicklei/proto" ) func protoToCUE(typ string, options []*proto.Option) ast.Expr { t, ok := scalars[typ] if !ok { return nil } return predeclared(t) } var scalars = map[string]string{ // Differing "sint32": "int32", "sint64": "int64", "fixed32": "uint32", "fixed64": "uint64", "sfixed32": "int32", "sfixed64": "int64", // Identical to CUE "int32": "int32", "int64": "int64", "uint32": "uint32", "uint64": "uint64", "double": "float64", "float": "float32", "bool": "bool", "string": "string", "bytes": "bytes", } func predeclared(s string) ast.Expr { return &ast.Ident{ Name: s, Node: ast.NewIdent("__" + s), } } func (p *protoConverter) setBuiltin(from string, to func() ast.Expr, pkg *protoConverter) { p.scope[0][from] = mapping{to, pkg} } func (p *protoConverter) setBuiltinParse(from, to string, pkg *protoConverter) { f := func() ast.Expr { expr, err := parser.ParseExpr("", to, parser.ParseComments) if err != nil { panic(fmt.Sprintf("error parsing name %q: %v", to, err)) } return expr } p.scope[0][from] = mapping{f, pkg} } var ( pkgTime = &protoConverter{cuePkgPath: "time"} pkgStruct = &protoConverter{cuePkgPath: "struct"} importTime = ast.NewImport(nil, "time") importStruct = ast.NewImport(nil, "struct") ) func (p *protoConverter) mapBuiltinPackage(file string) (found bool) { // Map some builtin types to their JSON/CUE mappings. switch file { case "cue/cue.proto": return true case "google/protobuf/struct.proto": p.setBuiltin("google.protobuf.Struct", func() ast.Expr { return ast.NewStruct() }, nil) p.setBuiltin("google.protobuf.Value", func() ast.Expr { return ast.NewIdent("_") }, nil) p.setBuiltin("google.protobuf.NullValue", func() ast.Expr { return ast.NewNull() }, nil) p.setBuiltin("google.protobuf.ListValue", func() ast.Expr { return ast.NewList(&ast.Ellipsis{}) }, nil) p.setBuiltin("google.protobuf.StringValue", func() ast.Expr { return predeclared("string") }, nil) p.setBuiltin("google.protobuf.BoolValue", func() ast.Expr { return predeclared("bool") }, nil) p.setBuiltin("google.protobuf.NumberValue", func() ast.Expr { return predeclared("number") }, nil) return true case "google/protobuf/empty.proto": f := func() ast.Expr { time := &ast.Ident{Name: "struct", Node: importStruct} return ast.NewCall( ast.NewSel(time, "MaxFields"), ast.NewLit(token.INT, "0"), ) } p.setBuiltin("google.protobuf.Empty", f, pkgStruct) return true case "google/protobuf/duration.proto": f := func() ast.Expr { time := &ast.Ident{Name: "time", Node: importTime} return ast.NewSel(time, "Duration") } p.setBuiltin("google.protobuf.Duration", f, pkgTime) return true case "google/protobuf/timestamp.proto": f := func() ast.Expr { time := &ast.Ident{Name: "time", Node: importTime} return ast.NewSel(time, "Time") } p.setBuiltin("google.protobuf.Timestamp", f, pkgTime) return true case "google/protobuf/any.proto": // TODO: technically, the value should be `_` (anything), but that // will not convert to a valid OpenAPI value. In practice, all // "well-known" types except wrapper types (which will likely not // be used here) are represented as strings. // // In Structural OpenAPI this type cannot be represented. p.setBuiltinParse("google.protobuf.Any", `{ // A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. This string must contain at least one "/" character. The last segment of the URL's path must represent the fully qualified name of the type (as in `+ "`type.googleapis.com/google.protobuf.Duration`"+`). The name should be in a canonical form (e.g., leading "." is not accepted). // The remaining fields of this object correspond to fields of the proto messsage. If the embedded message is well-known and has a custom JSON representation, that representation is assigned to the 'value' field. "@type": string, }`, nil) return true case "google/protobuf/wrappers.proto": p.setBuiltinParse("google.protobuf.DoubleValue", `null | float`, nil) p.setBuiltinParse("google.protobuf.FloatValue", `null | float`, nil) p.setBuiltinParse("google.protobuf.Int64Value", `null | int64`, nil) p.setBuiltinParse("google.protobuf.UInt64Value", `null | uint64`, nil) p.setBuiltinParse("google.protobuf.Int32Value", `null | int32`, nil) p.setBuiltinParse("google.protobuf.UInt32Value", `null | uint32`, nil) p.setBuiltinParse("google.protobuf.BoolValue", `null | bool`, nil) p.setBuiltinParse("google.protobuf.StringValue", `null | string`, nil) p.setBuiltinParse("google.protobuf.BytesValue", `null | bytes`, nil) return true // case "google/protobuf/field_mask.proto": // p.setBuiltin("google.protobuf.FieldMask", "protobuf.FieldMask", nil) // protobuf.Any } return false }