| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // 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 compiler |
| |
| import ( |
| "bytes" |
| "fmt" |
| "os/exec" |
| |
| "strings" |
| |
| "errors" |
| |
| "github.com/golang/protobuf/proto" |
| "github.com/golang/protobuf/ptypes/any" |
| ext_plugin "github.com/googleapis/gnostic/extensions" |
| yaml "gopkg.in/yaml.v2" |
| ) |
| |
| // ExtensionHandler describes a binary that is called by the compiler to handle specification extensions. |
| type ExtensionHandler struct { |
| Name string |
| } |
| |
| // HandleExtension calls a binary extension handler. |
| func HandleExtension(context *Context, in interface{}, extensionName string) (bool, *any.Any, error) { |
| handled := false |
| var errFromPlugin error |
| var outFromPlugin *any.Any |
| |
| if context != nil && context.ExtensionHandlers != nil && len(*(context.ExtensionHandlers)) != 0 { |
| for _, customAnyProtoGenerator := range *(context.ExtensionHandlers) { |
| outFromPlugin, errFromPlugin = customAnyProtoGenerator.handle(in, extensionName) |
| if outFromPlugin == nil { |
| continue |
| } else { |
| handled = true |
| break |
| } |
| } |
| } |
| return handled, outFromPlugin, errFromPlugin |
| } |
| |
| func (extensionHandlers *ExtensionHandler) handle(in interface{}, extensionName string) (*any.Any, error) { |
| if extensionHandlers.Name != "" { |
| binary, _ := yaml.Marshal(in) |
| |
| request := &ext_plugin.ExtensionHandlerRequest{} |
| |
| version := &ext_plugin.Version{} |
| version.Major = 0 |
| version.Minor = 1 |
| version.Patch = 0 |
| request.CompilerVersion = version |
| |
| request.Wrapper = &ext_plugin.Wrapper{} |
| |
| request.Wrapper.Version = "v2" |
| request.Wrapper.Yaml = string(binary) |
| request.Wrapper.ExtensionName = extensionName |
| |
| requestBytes, _ := proto.Marshal(request) |
| cmd := exec.Command(extensionHandlers.Name) |
| cmd.Stdin = bytes.NewReader(requestBytes) |
| output, err := cmd.Output() |
| |
| if err != nil { |
| fmt.Printf("Error: %+v\n", err) |
| return nil, err |
| } |
| response := &ext_plugin.ExtensionHandlerResponse{} |
| err = proto.Unmarshal(output, response) |
| if err != nil { |
| fmt.Printf("Error: %+v\n", err) |
| fmt.Printf("%s\n", string(output)) |
| return nil, err |
| } |
| if !response.Handled { |
| return nil, nil |
| } |
| if len(response.Error) != 0 { |
| message := fmt.Sprintf("Errors when parsing: %+v for field %s by vendor extension handler %s. Details %+v", in, extensionName, extensionHandlers.Name, strings.Join(response.Error, ",")) |
| return nil, errors.New(message) |
| } |
| return response.Value, nil |
| } |
| return nil, nil |
| } |