c65gm/internal/preproc/filereader.go

95 lines
2 KiB
Go

package preproc
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)
type FileReader interface {
ReadLines(includeSpec string, currentDir string) ([]string, error)
}
type DiskFileReader struct {
cache map[string][]string
libPath string
}
func NewDiskFileReader() *DiskFileReader {
return &DiskFileReader{
cache: make(map[string][]string),
libPath: os.Getenv("C65LIBPATH"),
}
}
func (d *DiskFileReader) ReadLines(includeSpec string, currentDir string) ([]string, error) {
path, err := d.resolvePath(includeSpec, currentDir)
if err != nil {
return nil, err
}
abs, err := filepath.Abs(path)
if err != nil {
return nil, err
}
if lines, ok := d.cache[abs]; ok {
return lines, nil
}
data, err := os.ReadFile(abs)
if err != nil {
return nil, err
}
lines := strings.Split(string(data), "\n")
d.cache[abs] = lines
return lines, nil
}
func (d *DiskFileReader) resolvePath(spec string, curDir string) (string, error) {
if len(spec) == 0 {
return "", errors.New("empty include path")
}
// <file> -> library include
if strings.HasPrefix(spec, "<") && strings.HasSuffix(spec, ">") {
base := strings.TrimSpace(spec[1 : len(spec)-1])
if d.libPath == "" {
return "", errors.New("C65LIBPATH not set for angle-bracket include")
}
path := filepath.Join(d.libPath, base)
if _, err := os.Stat(path); err != nil {
return "", fmt.Errorf("library include not found: %s", path)
}
return path, nil
}
// quoted or bare -> relative to current dir
base := strings.Trim(spec, `"'`)
path := filepath.Join(curDir, base)
if _, err := os.Stat(path); err != nil {
return "", fmt.Errorf("include not found: %s", path)
}
return path, nil
}
type MockFileReader struct {
files map[string][]string
}
func NewMockFileReader(files map[string][]string) *MockFileReader {
return &MockFileReader{
files: files,
}
}
func (m *MockFileReader) ReadLines(includeSpec string, _ string) ([]string, error) {
lines, ok := m.files[includeSpec]
if !ok {
return nil, fmt.Errorf("open %s: no such file or directory", includeSpec)
}
return lines, nil
}