trpc.group/trpc-go/trpc-cmdline@v1.0.9/config/def.go (about)

     1  // Tencent is pleased to support the open source community by making tRPC available.
     2  //
     3  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     4  // All rights reserved.
     5  //
     6  // If you have downloaded a copy of the tRPC source code from Tencent,
     7  // please note that tRPC source code is licensed under the  Apache 2.0 License,
     8  // A copy of the Apache 2.0 License is included in this file.
     9  
    10  package config
    11  
    12  import (
    13  	"fmt"
    14  	"io"
    15  	"net/http"
    16  	"os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"runtime"
    20  	"strings"
    21  
    22  	"trpc.group/trpc-go/trpc-cmdline/util/semver"
    23  )
    24  
    25  // IDLType is IDL type.
    26  type IDLType uint
    27  
    28  // Related constants of IDL type.
    29  const (
    30  	IDLTypeProtobuf    IDLType = iota // IDL is protobuf
    31  	IDLTypeFlatBuffers                // IDL is flatbuffers
    32  	IDLTypeInvalid
    33  )
    34  
    35  var idlTypeDesc = map[IDLType]string{
    36  	IDLTypeProtobuf:    "protobuf",
    37  	IDLTypeFlatBuffers: "flatbuffers",
    38  }
    39  
    40  // Valid Check if the IDL type is valid.
    41  func (t IDLType) Valid() bool {
    42  	return t < IDLTypeInvalid
    43  }
    44  
    45  // String Return the description of the IDL type.
    46  func (t IDLType) String() string {
    47  	if !t.Valid() {
    48  		return fmt.Sprintf("unknown type: %d", t)
    49  	}
    50  	return idlTypeDesc[t]
    51  }
    52  
    53  // Template is language templates.
    54  type Template struct {
    55  	Language    string `yaml:"language"`      // Programming language
    56  	LangFileExt string `yaml:"lang_file_ext"` // Source file extension for the programming language
    57  	// The output filename for the stub corresponds to: service name + separator + original file name
    58  	Separator string `yaml:"separator"`
    59  	// Keep the original file name as a suffix for the generated files
    60  	KeepOrigName bool `yaml:"keep_orig_name"`
    61  	// Follow the camelcase style for generated file names
    62  	CamelCaseName     bool     `yaml:"camelcase_name"`
    63  	AssetDir          string   `yaml:"asset_dir"`            // Code template directory
    64  	RPCServerStub     string   `yaml:"rpc_server_stub"`      // Server stub
    65  	RPCServerTestStub string   `yaml:"rpc_server_test_stub"` // Server test stub
    66  	RPCClientStub     []string `yaml:"rpc_client_stub"`      // Client stub
    67  	// Whether the client stub includes all service definitions
    68  	RPCClientStubPerService bool `yaml:"rpc_client_stub_per_service"`
    69  }
    70  
    71  // OpSys is the system of operation (运营体系).
    72  type OpSys struct {
    73  	Name    string   `yaml:"name"`
    74  	Imports []string `yaml:"imports"`
    75  }
    76  
    77  // Config is the global config.
    78  type Config struct {
    79  	Domain     string                          `yaml:"domain"` // host in importPath
    80  	TplFileExt string                          `yaml:"tpl_file_ext"`
    81  	IDL        map[string]*Dependency          `yaml:"idl"`       // IDL name -> IDL tool
    82  	Tools      map[string][]*Dependency        `yaml:"tools"`     // Programming language -> Dependency tools
    83  	Plugins    map[string][]string             `yaml:"plugins"`   // Programming language -> Dependency plugins
    84  	Templates  map[string]map[string]*Template `yaml:"templates"` // idltype -> Code templates for each language
    85  }
    86  
    87  // Dependency is the description of dependencies.
    88  type Dependency struct {
    89  	Executable  string `yaml:"executable"`   // Executable file name.
    90  	VersionMin  string `yaml:"version_min"`  // Min version.
    91  	VersionCmd  string `yaml:"version_cmd"`  // Max version.
    92  	ArtifactURL string `yaml:"artifact_url"` // Artifact download URL.
    93  	Repository  string `yaml:"repository"`   // Repository URL.
    94  	MD5         string `yaml:"md5"`          // md5 sum up.
    95  	Fallback    string `yaml:"fallback"`     // Failure prompt.
    96  }
    97  
    98  // TryInstallTo tries to install the dependency to the given path.
    99  func (d *Dependency) TryInstallTo(path string) error {
   100  	d.ArtifactURL = strings.ReplaceAll(d.ArtifactURL, "${os}", runtime.GOOS)
   101  	rsp, err := http.Get(d.ArtifactURL)
   102  	if err != nil {
   103  		return fmt.Errorf("install %s from %s err: %w", d.Executable, d.ArtifactURL, err)
   104  	}
   105  	defer rsp.Body.Close()
   106  	if rsp.StatusCode != http.StatusOK {
   107  		body, err := io.ReadAll(rsp.Body)
   108  		return fmt.Errorf("download from %s, rsp body: %q, body read err: %w", d.ArtifactURL, body, err)
   109  	}
   110  	dst := filepath.Join(path, d.Executable)
   111  	bs, err := io.ReadAll(rsp.Body)
   112  	if err != nil {
   113  		return fmt.Errorf("read from %s err: %w", d.ArtifactURL, err)
   114  	}
   115  	if err := os.WriteFile(dst, bs, os.ModePerm); err != nil {
   116  		return fmt.Errorf("write to %s err: %w", dst, err)
   117  	}
   118  	return nil
   119  }
   120  
   121  // Installed checks installed or not.
   122  func (d *Dependency) Installed() bool {
   123  	if _, err := exec.LookPath(d.Executable); err != nil {
   124  		return false
   125  	}
   126  	return true
   127  }
   128  
   129  // version returns installed version.
   130  func (d *Dependency) version() (string, error) {
   131  	if d.VersionCmd == "" {
   132  		return "", nil
   133  	}
   134  
   135  	cmd := exec.Command("sh", "-c", d.VersionCmd)
   136  	if runtime.GOOS == "windows" {
   137  		cmd = exec.Command("cmd", "/C", d.VersionCmd)
   138  	}
   139  
   140  	buf, err := cmd.CombinedOutput()
   141  	if err != nil {
   142  		return "", fmt.Errorf("read version failed: %w, command: %s", err, d.VersionCmd)
   143  	}
   144  
   145  	s := string(buf)
   146  	strs := versionRE.FindStringSubmatch(s)
   147  	if len(strs) < 2 {
   148  		return "", fmt.Errorf("version %s does not match pattern %s", s, versionPattern)
   149  	}
   150  	version := strings.Join(strs[1:], ".")
   151  	return version, nil
   152  }
   153  
   154  // CheckVersion check if installed version meet the version requirement.
   155  func (d *Dependency) CheckVersion() (passed bool, error error) {
   156  	// skip checking if cmd/version not specified
   157  	if d.VersionMin == "" || d.VersionCmd == "" {
   158  		return true, nil
   159  	}
   160  
   161  	// load version
   162  	v, err := d.version()
   163  	if err != nil {
   164  		return false, err
   165  	}
   166  
   167  	// compare version and required version
   168  	version := v
   169  	required := d.VersionMin
   170  
   171  	version = versionNumber(version)
   172  	required = versionNumber(required)
   173  	return semver.NewerThan(version, required), nil
   174  }
   175  
   176  func versionNumber(v string) string {
   177  	if len(v) != 0 && (v[0] == 'v' || v[0] == 'V') {
   178  		return v[1:]
   179  	}
   180  	return v
   181  }