github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/cmd/gstack/utils/utils.go (about)

     1  package utils
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/parser"
     7  	"go/token"
     8  	"io"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
    14  )
    15  
    16  type FunctionIDArg struct {
    17  	FuncDecl *ast.FuncDecl
    18  	ArgPos   token.Pos
    19  	ArgEnd   token.Pos
    20  }
    21  
    22  func ReadFile(filename string, info fs.FileInfo) ([]byte, error) {
    23  	f, err := os.Open(filename)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	defer f.Close()
    28  	size := int(info.Size())
    29  	src := make([]byte, size)
    30  	n, err := io.ReadFull(f, src)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	if n < size {
    35  		return nil, fmt.Errorf("error: size of %q changed during reading (from %d to %d bytes)", filename, size, n)
    36  	} else if n > size {
    37  		return nil, fmt.Errorf("error: size of %q changed during reading (from %d to >=%d bytes)", filename, size, len(src))
    38  	}
    39  
    40  	return src, nil
    41  }
    42  
    43  func FixSource(fset *token.FileSet, path string, src []byte, listOfArgs []FunctionIDArg) ([]byte, error) {
    44  	var fixed []byte
    45  	var previousArgEnd int
    46  	for _, arg := range listOfArgs {
    47  		argPosOffset := fset.Position(arg.ArgPos).Offset
    48  		argEndOffset := fset.Position(arg.ArgEnd).Offset
    49  		argument, err := makeCall(fset, path, arg)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		fixed = append(fixed, src[previousArgEnd:argPosOffset]...)
    54  		fixed = append(fixed, fmt.Sprintf("%q", argument)...)
    55  		previousArgEnd = argEndOffset
    56  	}
    57  	fixed = append(fixed, src[previousArgEnd:]...)
    58  
    59  	return fixed, nil
    60  }
    61  
    62  func WriteFile(filename string, formatted []byte, perm fs.FileMode) error {
    63  	fout, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, perm)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	defer fout.Close()
    69  
    70  	_, err = fout.Write(formatted)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func makeCall(fset *token.FileSet, path string, arg FunctionIDArg) (string, error) {
    79  	basePath := filepath.Join("github.com", "ydb-platform", version.Package, "v"+version.Major, "")
    80  	packageName, err := getPackageName(fset, arg)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  	filePath := filepath.Dir(filepath.Dir(path))
    85  	funcName, err := getFuncName(arg.FuncDecl)
    86  	if err != nil {
    87  		return "", err
    88  	}
    89  
    90  	return filepath.Join(basePath, filePath, packageName) + "." + funcName, nil
    91  }
    92  
    93  func getFuncName(funcDecl *ast.FuncDecl) (string, error) {
    94  	if funcDecl.Recv != nil {
    95  		recvType := funcDecl.Recv.List[0].Type
    96  		prefix, err := getIdentNameFromExpr(recvType)
    97  		if err != nil {
    98  			return "", err
    99  		}
   100  
   101  		return prefix + "." + funcDecl.Name.Name, nil
   102  	}
   103  
   104  	return funcDecl.Name.Name, nil
   105  }
   106  
   107  func getIdentNameFromExpr(expr ast.Expr) (string, error) {
   108  	switch expr := expr.(type) {
   109  	case *ast.Ident:
   110  		return expr.Name, nil
   111  	case *ast.StarExpr:
   112  		prefix, err := getIdentNameFromExpr(expr.X)
   113  		if err != nil {
   114  			return "", err
   115  		}
   116  
   117  		return "(*" + prefix + ")", nil
   118  	case *ast.IndexExpr:
   119  		return getIdentNameFromExpr(expr.X)
   120  	case *ast.IndexListExpr:
   121  		return getIdentNameFromExpr(expr.X)
   122  	default:
   123  		return "", fmt.Errorf("error during getting ident from expr")
   124  	}
   125  }
   126  
   127  func getPackageName(fset *token.FileSet, arg FunctionIDArg) (string, error) {
   128  	file := fset.File(arg.ArgPos)
   129  	parsedFile, err := parser.ParseFile(fset, file.Name(), nil, parser.PackageClauseOnly)
   130  	if err != nil {
   131  		return "", fmt.Errorf("error during get package name function")
   132  	}
   133  
   134  	return parsedFile.Name.Name, nil
   135  }