go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/descriptor-adapter/generator.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     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  // descriptor-generator generates all boiler-plate code needed to adapt type-safe
    16  // KV descriptor for the KVDescriptor structure definition.
    17  //
    18  // To use the generator, add go generate command into your descriptor as a comment:
    19  //  //go:generate descriptor-adapter --descriptor-name <descriptor-name> --value-type <typename> [--meta-type <typename>] [--output-dir <path>] [--import <path>]...
    20  //
    21  // Note: import paths can be relative to the file with the go:generate comment.
    22  
    23  package main
    24  
    25  import (
    26  	"bytes"
    27  	"flag"
    28  	"fmt"
    29  	"os"
    30  	"path/filepath"
    31  	"strings"
    32  	"text/template"
    33  )
    34  
    35  // ArrayFlag implements repeated flag.
    36  type ArrayFlag struct {
    37  	values []string
    38  }
    39  
    40  // String return human-readable string representation of the array of flags.
    41  func (af *ArrayFlag) String() string {
    42  	str := "["
    43  	for idx, value := range af.values {
    44  		str += value
    45  		if idx < len(af.values)-1 {
    46  			str += ", "
    47  		}
    48  	}
    49  	str += "]"
    50  	return str
    51  }
    52  
    53  // Set add value into the array.
    54  func (af *ArrayFlag) Set(value string) error {
    55  	af.values = append(af.values, value)
    56  	return nil
    57  }
    58  
    59  var (
    60  	imports ArrayFlag
    61  
    62  	outputDirFlag      = flag.String("output-dir", ".", "Output directory where adapter package will be generated.")
    63  	descriptorNameFlag = flag.String("descriptor-name", "", "Name of the descriptor.")
    64  	valueTypeFlag      = flag.String("value-type", "", "Type of the described values.")
    65  	metaTypeFlag       = flag.String("meta-type", "interface{}", "Type of the metadata used by the descriptor.")
    66  )
    67  
    68  // TemplateData encapsulates input arguments for the template.
    69  type TemplateData struct {
    70  	DescriptorName string
    71  	ValueT         string
    72  	MetadataT      string
    73  	Imports        []string
    74  }
    75  
    76  // PathExists return true if the given path already exist in the file system.
    77  func PathExists(path string) bool {
    78  	_, err := os.Stat(path)
    79  	return !os.IsNotExist(err)
    80  }
    81  
    82  func main() {
    83  	flag.Var(&imports, "import", "Package to be imported in the generated adapter (can be relative path).")
    84  	flag.Parse()
    85  
    86  	// prepare input data for the template
    87  	inputData := TemplateData{
    88  		DescriptorName: *descriptorNameFlag,
    89  		ValueT:         *valueTypeFlag,
    90  		MetadataT:      *metaTypeFlag,
    91  	}
    92  
    93  	// expand relative import paths
    94  	gopath := os.Getenv("GOPATH")
    95  	cwd, err := os.Getwd()
    96  	if err != nil {
    97  		fmt.Fprintln(os.Stderr, "ERROR: ", err)
    98  		os.Exit(2)
    99  	}
   100  
   101  	for _, importPath := range imports.values {
   102  		if !PathExists(filepath.Join(gopath, "src", importPath)) {
   103  			asRelative := filepath.Join(cwd, importPath)
   104  			if PathExists(asRelative) {
   105  				importPath = filepath.Clean(asRelative)
   106  				importPath = strings.TrimPrefix(importPath, gopath+"/src")
   107  				importPath = strings.TrimLeft(importPath, "/")
   108  			}
   109  		}
   110  		inputData.Imports = append(inputData.Imports, importPath)
   111  	}
   112  
   113  	if inputData.ValueT == "" || inputData.DescriptorName == "" {
   114  		fmt.Fprintln(os.Stderr, "ERROR: value-type and descriptor-name must be specified")
   115  		os.Exit(1)
   116  	}
   117  
   118  	// generate adapter source code from the template
   119  	var buf bytes.Buffer
   120  	t := template.Must(template.New("").Parse(adapterTemplate))
   121  	err = t.Execute(&buf, inputData)
   122  	if err != nil {
   123  		fmt.Fprintln(os.Stderr, "ERROR: ", err)
   124  		os.Exit(2)
   125  	}
   126  
   127  	// prepare directory for the generated adapter
   128  	directory := *outputDirFlag + "/adapter/"
   129  	err = os.MkdirAll(directory, 0777)
   130  	if err != nil {
   131  		fmt.Fprintln(os.Stderr, "ERROR: ", err)
   132  		os.Exit(3)
   133  	}
   134  
   135  	// output the generated adapter into the file
   136  	filename := directory + "/" + strings.ToLower(*descriptorNameFlag) + ".go"
   137  	err = os.WriteFile(filename, buf.Bytes(), 0644)
   138  	if err != nil {
   139  		fmt.Fprintln(os.Stderr, "ERROR: ", err)
   140  		os.Exit(4)
   141  	}
   142  }