zilch/lang/go/utils/parser/main.go

290 lines
5.7 KiB
Go
Raw Normal View History

2024-10-03 23:57:22 +00:00
package main
import (
"encoding/json"
"fmt"
"go/build"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
type WrappedStat struct {
fs.FileInfo
newName string // meow
}
func (s *WrappedStat) Name() string {
return s.newName
}
type DirStat struct {
name string
}
func (s *DirStat) Name() string {
return s.name
}
func (s *DirStat) Size() int64 {
return 1
}
func (s *DirStat) Mode() fs.FileMode {
return fs.FileMode(fs.ModeDir | 0777)
}
func (s *DirStat) ModTime() time.Time {
return time.Time{}
}
func (s *DirStat) IsDir() bool {
return true
}
func (s *DirStat) Sys() any {
return "zilch"
}
type Input struct {
// directory -> filename -> path
Files map[string]map[string]string `json:"files"`
GOARCH string `json:"GOARCH"`
GOOS string `json:"GOOS"`
}
type Output struct {
Name string `json:"name"`
GoFiles []string `json:"goFiles"`
SFiles []string `json:"sFiles"`
Imports []string `json:"imports"`
Embeds map[string][][]string `json:"embeds"`
}
func main() {
inputFile, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
var input Input
err = json.NewDecoder(inputFile).Decode(&input)
if err != nil {
panic(err)
}
ctx := build.Context{
GOARCH: input.GOARCH,
GOOS: input.GOOS,
Compiler: "gc",
ToolTags: build.Default.ToolTags,
ReleaseTags: build.Default.ReleaseTags,
ReadDir: func(dir string) ([]fs.FileInfo, error) {
fmt.Printf("ReadDir(%q)\n", dir)
if !strings.HasPrefix(dir, "/code") {
return nil, fs.ErrNotExist
}
dir = path.Clean(dir[5:])
if dir == "." {
dir = "/"
}
if !strings.HasSuffix(dir, "/") {
dir += "/"
}
dircontents, ok := input.Files[dir]
if !ok {
return nil, fs.ErrNotExist
}
infos := make([]fs.FileInfo, len(dircontents))
i := 0
for name, file := range dircontents {
stat, err := os.Stat(file)
if err != nil {
return nil, err
}
infos[i] = &WrappedStat{FileInfo: stat, newName: name}
i = i + 1
}
for key := range input.Files {
if path.Dir(key) == dir {
base := path.Base(key)
if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
continue
}
infos = append(infos, &DirStat{base})
}
}
return infos, nil
},
OpenFile: func(pth string) (io.ReadCloser, error) {
opth := pth
fmt.Printf("OpenFile(%q)\n", pth)
if !strings.HasPrefix(pth, "/code") {
return nil, fs.ErrNotExist
}
pth = path.Clean(pth[5:])
dirname, fname := path.Split(pth)
if dirname == "." {
dirname = "/"
}
dir := input.Files[dirname]
data, err := os.Open(dir[fname])
if err != nil {
return data, fmt.Errorf("OpenFile(%q; %q[%q]; %q): %w", opth, dirname, fname, dir[fname], err)
}
return data, err
},
IsDir: func(dir string) bool {
fmt.Printf("IsDir(%q)\n", dir)
if !strings.HasPrefix(dir, "/code") {
return false
}
dir = path.Clean(dir[5:])
if dir == "." {
dir = "/"
}
if !strings.HasSuffix(dir, "/") {
dir += "/"
}
_, ok := input.Files[dir]
fmt.Printf("IsDir -> %q, %v\n", dir, ok)
return ok
},
HasSubdir: func(root, dir string) (rel string, ok bool) {
root = path.Clean(root)
dir = path.Clean(dir)
return strings.CutPrefix(dir, root)
},
}
var filenames []string
for dirname, files := range input.Files {
for filename := range files {
filenames = append(filenames, filepath.Join(dirname, filename))
}
}
files := make(map[string]Output)
for dir, filelist := range input.Files {
isGo := false
for file := range filelist {
if strings.HasSuffix(file, ".go") {
isGo = true
break
}
}
fmt.Printf("Checking %q..\n", dir)
if strings.Contains(dir, "/.") || strings.Contains(dir, "/_") || strings.Contains(dir, "/testdata/") {
fmt.Printf(" skipping; \n")
continue
}
base := path.Base(dir)
if !isGo || strings.HasPrefix(base, "_") || strings.HasPrefix(base, ".") || base == "testdata" {
fmt.Printf(" skipping (not go)\n")
continue
}
pkg, err := ctx.Import(".", path.Clean("/code"+dir), 0)
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
continue
}
panic(err)
}
out := Output{
Name: pkg.Name,
Imports: pkg.Imports,
GoFiles: pkg.GoFiles,
SFiles: pkg.SFiles,
Embeds: make(map[string][][]string),
}
// _test only, or so
if len(pkg.GoFiles) == 0 {
continue
}
sort.Strings(out.Imports)
sort.Strings(out.GoFiles)
sort.Strings(out.SFiles)
sort.Strings(pkg.EmbedPatterns)
for _, pattern := range pkg.EmbedPatterns {
matchedFiles := []string{}
// TODO: proper matching
if strings.HasPrefix(pattern, "all:") {
pattern = pattern[4:]
}
fullPattern := filepath.Join(dir, pattern)
for _, file := range filenames {
if ok, _ := filepath.Match(fullPattern, file); ok {
matchedFiles = append(matchedFiles, file)
}
}
if len(matchedFiles) == 0 {
dirpattern := fullPattern + "/"
for _, file := range filenames {
if strings.HasPrefix(file, dirpattern) {
matchedFiles = append(matchedFiles, file)
}
if file == fullPattern {
matchedFiles = append(matchedFiles, file)
}
}
}
sort.Strings(matchedFiles)
var split [][]string
for _, match := range matchedFiles {
split = append(split, []string{match[len(dir):], filepath.Dir(match) + "/", filepath.Base(match)})
}
out.Embeds[pattern] = split
}
files[dir] = out
}
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
if err := json.NewEncoder(out).Encode(files); err != nil {
panic(err)
}
out.Close()
}