feat: publish the Sandwich Hime source preview
Signed-off-by: Cole Speelman <gamertan@noreply.localhost>
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var excludedDirectories = map[string]bool{
|
||||
".git": true,
|
||||
".hg": true,
|
||||
".svn": true,
|
||||
"vendor": true,
|
||||
}
|
||||
|
||||
func discover(ctx context.Context, paths []string) ([]string, []Diagnostic) {
|
||||
if len(paths) == 0 {
|
||||
paths = []string{"."}
|
||||
}
|
||||
filesByAbsolutePath := make(map[string]string)
|
||||
var diagnostics []Diagnostic
|
||||
|
||||
for _, requested := range paths {
|
||||
if err := ctx.Err(); err != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(requested, sourcePosition{Line: 1, Column: 1}, "HIM2001", "operation canceled: "+err.Error()))
|
||||
break
|
||||
}
|
||||
clean := filepath.Clean(requested)
|
||||
if symlinkParent, symlinkErr := firstSymlinkComponent(clean); symlinkErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2002", "cannot inspect path ancestry: "+symlinkErr.Error()))
|
||||
continue
|
||||
} else if symlinkParent != "" {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2003", fmt.Sprintf("symlink paths are not followed (through %s)", symlinkParent)))
|
||||
continue
|
||||
}
|
||||
info, err := os.Lstat(clean)
|
||||
if err != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2002", "cannot inspect path: "+err.Error()))
|
||||
continue
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2003", "symlink paths are not followed"))
|
||||
continue
|
||||
}
|
||||
if !info.IsDir() {
|
||||
if filepath.Ext(clean) != ".sando" {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2004", "explicit source file must use the .sando extension"))
|
||||
continue
|
||||
}
|
||||
absolute, absoluteErr := filepath.Abs(clean)
|
||||
if absoluteErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(clean, sourcePosition{Line: 1, Column: 1}, "HIM2005", "cannot resolve source path: "+absoluteErr.Error()))
|
||||
continue
|
||||
}
|
||||
filesByAbsolutePath[absolute] = clean
|
||||
continue
|
||||
}
|
||||
|
||||
root := clean
|
||||
rootInfo := info
|
||||
walkErr := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if walkErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2006", "cannot inspect path during discovery: "+walkErr.Error()))
|
||||
if entry != nil && entry.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if path != root && entry.IsDir() && excludedDirectories[entry.Name()] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if entry.Type()&os.ModeSymlink != 0 {
|
||||
if entry.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if path != root && entry.IsDir() {
|
||||
entryInfo, statErr := entry.Info()
|
||||
if statErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2007", "cannot inspect directory: "+statErr.Error()))
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !sameFilesystem(rootInfo, entryInfo) {
|
||||
diagnostics = append(diagnostics, Diagnostic{Path: path, Line: 1, Column: 1, Code: "HIM2901", Severity: SeverityWarning, Message: "skipped mounted filesystem boundary"})
|
||||
return filepath.SkipDir
|
||||
}
|
||||
for _, marker := range []string{".git", ".hg", ".svn"} {
|
||||
markerPath := filepath.Join(path, marker)
|
||||
if _, markerErr := os.Lstat(markerPath); markerErr == nil {
|
||||
return filepath.SkipDir
|
||||
} else if !os.IsNotExist(markerErr) {
|
||||
diagnostics = append(diagnostics, diagnostic(markerPath, sourcePosition{Line: 1, Column: 1}, "HIM2011", "cannot inspect nested VCS boundary: "+markerErr.Error()))
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
modulePath := filepath.Join(path, "go.mod")
|
||||
if moduleInfo, moduleErr := os.Lstat(modulePath); moduleErr == nil {
|
||||
if !moduleInfo.Mode().IsRegular() {
|
||||
diagnostics = append(diagnostics, diagnostic(modulePath, sourcePosition{Line: 1, Column: 1}, "HIM2008", "nested go.mod boundary is not a regular file; directory was skipped"))
|
||||
}
|
||||
return filepath.SkipDir
|
||||
} else if !os.IsNotExist(moduleErr) {
|
||||
diagnostics = append(diagnostics, diagnostic(modulePath, sourcePosition{Line: 1, Column: 1}, "HIM2008", "cannot inspect nested module boundary: "+moduleErr.Error()))
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
if entry.IsDir() || filepath.Ext(entry.Name()) != ".sando" {
|
||||
return nil
|
||||
}
|
||||
entryInfo, statErr := entry.Info()
|
||||
if statErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2009", "cannot inspect source: "+statErr.Error()))
|
||||
return nil
|
||||
}
|
||||
if !entryInfo.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
absolute, absoluteErr := filepath.Abs(path)
|
||||
if absoluteErr != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2005", "cannot resolve source path: "+absoluteErr.Error()))
|
||||
return nil
|
||||
}
|
||||
filesByAbsolutePath[absolute] = path
|
||||
return nil
|
||||
})
|
||||
if walkErr != nil && ctx.Err() != nil {
|
||||
diagnostics = append(diagnostics, diagnostic(root, sourcePosition{Line: 1, Column: 1}, "HIM2001", "operation canceled: "+ctx.Err().Error()))
|
||||
}
|
||||
}
|
||||
|
||||
absolutePaths := make([]string, 0, len(filesByAbsolutePath))
|
||||
for absolute := range filesByAbsolutePath {
|
||||
absolutePaths = append(absolutePaths, absolute)
|
||||
}
|
||||
sort.Strings(absolutePaths)
|
||||
discovered := make([]string, 0, len(absolutePaths))
|
||||
for _, absolute := range absolutePaths {
|
||||
discovered = append(discovered, filesByAbsolutePath[absolute])
|
||||
}
|
||||
sort.SliceStable(discovered, func(i, j int) bool {
|
||||
left, _ := filepath.Abs(discovered[i])
|
||||
right, _ := filepath.Abs(discovered[j])
|
||||
return filepath.ToSlash(left) < filepath.ToSlash(right)
|
||||
})
|
||||
sortDiagnostics(diagnostics)
|
||||
return discovered, diagnostics
|
||||
}
|
||||
|
||||
func firstSymlinkComponent(path string) (string, error) {
|
||||
absolute, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
volume := filepath.VolumeName(absolute)
|
||||
remainder := strings.TrimPrefix(absolute, volume)
|
||||
remainder = strings.TrimPrefix(remainder, string(filepath.Separator))
|
||||
current := volume + string(filepath.Separator)
|
||||
for _, component := range strings.Split(remainder, string(filepath.Separator)) {
|
||||
if component == "" {
|
||||
continue
|
||||
}
|
||||
current = filepath.Join(current, component)
|
||||
info, lstatErr := os.Lstat(current)
|
||||
if lstatErr != nil {
|
||||
return "", lstatErr
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return current, nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func sameFilesystem(root, candidate fs.FileInfo) bool {
|
||||
rootDevice, rootOK := deviceNumber(root.Sys())
|
||||
candidateDevice, candidateOK := deviceNumber(candidate.Sys())
|
||||
return !rootOK || !candidateOK || rootDevice == candidateDevice
|
||||
}
|
||||
|
||||
func deviceNumber(system any) (uint64, bool) {
|
||||
if system == nil {
|
||||
return 0, false
|
||||
}
|
||||
value := reflect.Indirect(reflect.ValueOf(system))
|
||||
if !value.IsValid() || value.Kind() != reflect.Struct {
|
||||
return 0, false
|
||||
}
|
||||
field := value.FieldByName("Dev")
|
||||
if !field.IsValid() {
|
||||
return 0, false
|
||||
}
|
||||
switch field.Kind() {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return field.Uint(), true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
device := field.Int()
|
||||
if device < 0 {
|
||||
return 0, false
|
||||
}
|
||||
return uint64(device), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user