github.com/solo-io/cue@v0.4.7/encoding/protobuf/types.go (about)

     1  // Copyright 2019 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package protobuf
    16  
    17  import (
    18  	"fmt"
    19  	"text/scanner"
    20  
    21  	"github.com/emicklei/proto"
    22  	"github.com/solo-io/cue/cue/ast"
    23  	"github.com/solo-io/cue/cue/parser"
    24  	"github.com/solo-io/cue/cue/token"
    25  )
    26  
    27  func protoToCUE(typ string, options []*proto.Option) ast.Expr {
    28  	t, ok := scalars[typ]
    29  	if !ok {
    30  		return nil
    31  	}
    32  	return predeclared(t)
    33  }
    34  
    35  var scalars = map[string]string{
    36  	// Differing
    37  	"sint32":   "int32",
    38  	"sint64":   "int64",
    39  	"fixed32":  "uint32",
    40  	"fixed64":  "uint64",
    41  	"sfixed32": "int32",
    42  	"sfixed64": "int64",
    43  
    44  	// Identical to CUE
    45  	"int32":  "int32",
    46  	"int64":  "int64",
    47  	"uint32": "uint32",
    48  	"uint64": "uint64",
    49  
    50  	"double": "float64",
    51  	"float":  "float32",
    52  
    53  	"bool":   "bool",
    54  	"string": "string",
    55  	"bytes":  "bytes",
    56  }
    57  
    58  func predeclared(s string) ast.Expr {
    59  	return &ast.Ident{
    60  		Name: s,
    61  		Node: ast.NewIdent("__" + s),
    62  	}
    63  }
    64  
    65  func (p *protoConverter) setBuiltin(from string, to func() ast.Expr, pkg *protoConverter) {
    66  	p.scope[0][from] = mapping{to, pkg}
    67  }
    68  
    69  func (p *protoConverter) setBuiltinParse(from, to string, pkg *protoConverter) {
    70  	f := func() ast.Expr {
    71  		expr, err := parser.ParseExpr("", to, parser.ParseComments)
    72  		if err != nil {
    73  			panic(fmt.Sprintf("error parsing name %q: %v", to, err))
    74  		}
    75  		return expr
    76  	}
    77  	p.scope[0][from] = mapping{f, pkg}
    78  }
    79  
    80  var (
    81  	pkgTime      = &protoConverter{cuePkgPath: "time"}
    82  	pkgStruct    = &protoConverter{cuePkgPath: "struct"}
    83  	importTime   = ast.NewImport(nil, "time")
    84  	importStruct = ast.NewImport(nil, "struct")
    85  )
    86  
    87  func (p *protoConverter) mapBuiltinPackage(pos scanner.Position, file string, required bool) (generate bool) {
    88  	// Map some builtin types to their JSON/CUE mappings.
    89  	switch file {
    90  	case "gogoproto/gogo.proto":
    91  
    92  	case "google/protobuf/struct.proto":
    93  		p.setBuiltin("google.protobuf.Struct", func() ast.Expr {
    94  			return ast.NewStruct()
    95  		}, nil)
    96  
    97  		p.setBuiltin("google.protobuf.Value", func() ast.Expr {
    98  			return ast.NewIdent("_")
    99  		}, nil)
   100  
   101  		p.setBuiltin("google.protobuf.NullValue", func() ast.Expr {
   102  			return ast.NewNull()
   103  		}, nil)
   104  
   105  		p.setBuiltin("google.protobuf.ListValue", func() ast.Expr {
   106  			return ast.NewList(&ast.Ellipsis{})
   107  		}, nil)
   108  
   109  		p.setBuiltin("google.protobuf.StringValue", func() ast.Expr {
   110  			return predeclared("string")
   111  		}, nil)
   112  
   113  		p.setBuiltin("google.protobuf.BoolValue", func() ast.Expr {
   114  			return predeclared("bool")
   115  		}, nil)
   116  
   117  		p.setBuiltin("google.protobuf.NumberValue", func() ast.Expr {
   118  			return predeclared("number")
   119  		}, nil)
   120  
   121  		return false
   122  
   123  	case "google/protobuf/empty.proto":
   124  		f := func() ast.Expr {
   125  			time := &ast.Ident{Name: "struct", Node: importStruct}
   126  			return ast.NewCall(
   127  				ast.NewSel(time, "MaxFields"),
   128  				ast.NewLit(token.INT, "0"),
   129  			)
   130  		}
   131  		p.setBuiltin("google.protobuf.Empty", f, pkgStruct)
   132  		return false
   133  
   134  	case "google/protobuf/duration.proto":
   135  		f := func() ast.Expr {
   136  			time := &ast.Ident{Name: "time", Node: importTime}
   137  			return ast.NewSel(time, "Duration")
   138  		}
   139  		p.setBuiltin("google.protobuf.Duration", f, pkgTime)
   140  		return false
   141  
   142  	case "google/protobuf/timestamp.proto":
   143  		f := func() ast.Expr {
   144  			time := &ast.Ident{Name: "time", Node: importTime}
   145  			return ast.NewSel(time, "Time")
   146  		}
   147  		p.setBuiltin("google.protobuf.Timestamp", f, pkgTime)
   148  		return false
   149  
   150  	case "google/protobuf/any.proto":
   151  		// TODO: technically, the value should be `_` (anything), but that
   152  		// will not convert to a valid OpenAPI value. In practice, all
   153  		// "well-known" types except wrapper types (which will likely not
   154  		// be used here) are represented as strings.
   155  		//
   156  		// In Structural OpenAPI this type cannot be represented.
   157  		p.setBuiltinParse("google.protobuf.Any", `{
   158  	// 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 `+
   159  			"`type.googleapis.com/google.protobuf.Duration`"+`). The name should be in a canonical form (e.g., leading "." is not accepted).
   160  	// 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.
   161  	"@type": string,
   162  }`, nil)
   163  		return false
   164  
   165  	case "google/protobuf/wrappers.proto":
   166  		p.setBuiltinParse("google.protobuf.DoubleValue", `null | float`, nil)
   167  		p.setBuiltinParse("google.protobuf.FloatValue", `null | float`, nil)
   168  		p.setBuiltinParse("google.protobuf.Int64Value", `null | int64`, nil)
   169  		p.setBuiltinParse("google.protobuf.UInt64Value", `null | uint64`, nil)
   170  		p.setBuiltinParse("google.protobuf.Int32Value", `null | int32`, nil)
   171  		p.setBuiltinParse("google.protobuf.UInt32Value", `null | uint32`, nil)
   172  		p.setBuiltinParse("google.protobuf.BoolValue", `null | bool`, nil)
   173  		p.setBuiltinParse("google.protobuf.StringValue", `null | string`, nil)
   174  		p.setBuiltinParse("google.protobuf.BytesValue", `null | bytes`, nil)
   175  		return false
   176  
   177  	// case "google/protobuf/field_mask.proto":
   178  	// 	p.setBuiltin("google.protobuf.FieldMask", "protobuf.FieldMask", nil)
   179  
   180  	// 	protobuf.Any
   181  
   182  	default:
   183  		if required {
   184  			failf(pos, "import %q not found", file)
   185  		}
   186  	}
   187  	return true
   188  }