github.com/xraypb/xray-core@v1.6.6/common/common.go (about)

     1  // Package common contains common utilities that are shared among other packages.
     2  // See each sub-package for detail.
     3  package common
     4  
     5  import (
     6  	"fmt"
     7  	"go/build"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/xraypb/xray-core/common/errors"
    13  )
    14  
    15  //go:generate go run github.com/xraypb/xray-core/common/errors/errorgen
    16  
    17  // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
    18  var ErrNoClue = errors.New("not enough information for making a decision")
    19  
    20  // Must panics if err is not nil.
    21  func Must(err error) {
    22  	if err != nil {
    23  		panic(err)
    24  	}
    25  }
    26  
    27  // Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
    28  func Must2(v interface{}, err error) interface{} {
    29  	Must(err)
    30  	return v
    31  }
    32  
    33  // Error2 returns the err from the 2nd parameter.
    34  func Error2(v interface{}, err error) error {
    35  	return err
    36  }
    37  
    38  // envFile returns the name of the Go environment configuration file.
    39  // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
    40  func envFile() (string, error) {
    41  	if file := os.Getenv("GOENV"); file != "" {
    42  		if file == "off" {
    43  			return "", fmt.Errorf("GOENV=off")
    44  		}
    45  		return file, nil
    46  	}
    47  	dir, err := os.UserConfigDir()
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  	if dir == "" {
    52  		return "", fmt.Errorf("missing user-config dir")
    53  	}
    54  	return filepath.Join(dir, "go", "env"), nil
    55  }
    56  
    57  // GetRuntimeEnv returns the value of runtime environment variable,
    58  // that is set by running following command: `go env -w key=value`.
    59  func GetRuntimeEnv(key string) (string, error) {
    60  	file, err := envFile()
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  	if file == "" {
    65  		return "", fmt.Errorf("missing runtime env file")
    66  	}
    67  	var data []byte
    68  	var runtimeEnv string
    69  	data, readErr := os.ReadFile(file)
    70  	if readErr != nil {
    71  		return "", readErr
    72  	}
    73  	envStrings := strings.Split(string(data), "\n")
    74  	for _, envItem := range envStrings {
    75  		envItem = strings.TrimSuffix(envItem, "\r")
    76  		envKeyValue := strings.Split(envItem, "=")
    77  		if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) {
    78  			runtimeEnv = strings.TrimSpace(envKeyValue[1])
    79  		}
    80  	}
    81  	return runtimeEnv, nil
    82  }
    83  
    84  // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty.
    85  func GetGOBIN() string {
    86  	// The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command`
    87  	GOBIN := os.Getenv("GOBIN")
    88  	if GOBIN == "" {
    89  		var err error
    90  		// The one set by user by running `go env -w GOBIN=/path`
    91  		GOBIN, err = GetRuntimeEnv("GOBIN")
    92  		if err != nil {
    93  			// The default one that Golang uses
    94  			return filepath.Join(build.Default.GOPATH, "bin")
    95  		}
    96  		if GOBIN == "" {
    97  			return filepath.Join(build.Default.GOPATH, "bin")
    98  		}
    99  		return GOBIN
   100  	}
   101  	return GOBIN
   102  }
   103  
   104  // GetGOPATH returns GOPATH environment variable as a string. It will NOT be empty.
   105  func GetGOPATH() string {
   106  	// The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command`
   107  	GOPATH := os.Getenv("GOPATH")
   108  	if GOPATH == "" {
   109  		var err error
   110  		// The one set by user by running `go env -w GOPATH=/path`
   111  		GOPATH, err = GetRuntimeEnv("GOPATH")
   112  		if err != nil {
   113  			// The default one that Golang uses
   114  			return build.Default.GOPATH
   115  		}
   116  		if GOPATH == "" {
   117  			return build.Default.GOPATH
   118  		}
   119  		return GOPATH
   120  	}
   121  	return GOPATH
   122  }
   123  
   124  // GetModuleName returns the value of module in `go.mod` file.
   125  func GetModuleName(pathToProjectRoot string) (string, error) {
   126  	var moduleName string
   127  	loopPath := pathToProjectRoot
   128  	for {
   129  		if idx := strings.LastIndex(loopPath, string(filepath.Separator)); idx >= 0 {
   130  			gomodPath := filepath.Join(loopPath, "go.mod")
   131  			gomodBytes, err := os.ReadFile(gomodPath)
   132  			if err != nil {
   133  				loopPath = loopPath[:idx]
   134  				continue
   135  			}
   136  
   137  			gomodContent := string(gomodBytes)
   138  			moduleIdx := strings.Index(gomodContent, "module ")
   139  			newLineIdx := strings.Index(gomodContent, "\n")
   140  
   141  			if moduleIdx >= 0 {
   142  				if newLineIdx >= 0 {
   143  					moduleName = strings.TrimSpace(gomodContent[moduleIdx+6 : newLineIdx])
   144  					moduleName = strings.TrimSuffix(moduleName, "\r")
   145  				} else {
   146  					moduleName = strings.TrimSpace(gomodContent[moduleIdx+6:])
   147  				}
   148  				return moduleName, nil
   149  			}
   150  			return "", fmt.Errorf("can not get module path in `%s`", gomodPath)
   151  		}
   152  		break
   153  	}
   154  	return moduleName, fmt.Errorf("no `go.mod` file in every parent directory of `%s`", pathToProjectRoot)
   155  }