cloudflared-mirror/vendor/github.com/sudarshan-reddy/go-sumtype/decl.go

111 lines
2.7 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"golang.org/x/tools/go/packages"
)
// sumTypeDecl is a declaration of a sum type in a Go source file.
type sumTypeDecl struct {
// The package path that contains this decl.
Package *packages.Package
// The type named by this decl.
TypeName string
// The file path where this declaration was found.
Path string
// The line number where this declaration was found.
Line int
}
// Location returns a short string describing where this declaration was found.
func (d sumTypeDecl) Location() string {
return fmt.Sprintf("%s:%d", d.Path, d.Line)
}
// findSumTypeDecls searches every package given for sum type declarations of
// the form `go-sumtype:decl ...`.
func findSumTypeDecls(pkgs []*packages.Package) ([]sumTypeDecl, error) {
var decls []sumTypeDecl
for _, pkg := range pkgs {
for _, filename := range pkg.CompiledGoFiles {
if filepath.Base(filename) == "C" {
// ignore (fake?) cgo files
continue
}
fileDecls, err := sumTypeDeclSearch(filename)
if err != nil {
return nil, err
}
for i := range fileDecls {
fileDecls[i].Package = pkg
}
decls = append(decls, fileDecls...)
}
}
return decls, nil
}
// sumTypeDeclSearch searches the given file for sum type declarations of the
// form `go-sumtype:decl ...`.
func sumTypeDeclSearch(path string) ([]sumTypeDecl, error) {
var decls []sumTypeDecl
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
lineNum := 0
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lineNum++
line := scanner.Bytes()
if !isSumTypeDecl(line) {
continue
}
ty := parseSumTypeDecl(line)
if len(ty) == 0 {
continue
}
decls = append(decls, sumTypeDecl{
TypeName: ty,
Path: path,
Line: lineNum,
})
}
if err := scanner.Err(); err != nil {
// A scanner can puke if it hits a line that is too long.
// We assume such files won't contain any future decls and
// otherwise move on.
log.Printf("scan error reading '%s': %s", path, err)
}
return decls, nil
}
var reParseSumTypeDecl = regexp.MustCompile(`^//go-sumtype:decl\s+(\S+)\s*$`)
// parseSumTypeDecl parses the type name out of a sum type decl.
//
// If no such decl could be found, then this returns an empty string.
func parseSumTypeDecl(line []byte) string {
caps := reParseSumTypeDecl.FindSubmatch(line)
if len(caps) < 2 {
return ""
}
return string(caps[1])
}
// isSumTypeDecl returns true if and only if this line in a Go source file
// is a sum type decl.
func isSumTypeDecl(line []byte) bool {
variant1, variant2 := []byte("//go-sumtype:decl "), []byte("//go-sumtype:decl\t")
return bytes.HasPrefix(line, variant1) || bytes.HasPrefix(line, variant2)
}