blob: f605be91d01c88b4ad20260dab6e7b9aa39f49aa [file] [log] [blame]
/*
Copyright © 2021 Aspect Build Systems Inc
Not licensed for re-use.
*/
package system
import (
"fmt"
"io/fs"
"io/ioutil"
"os"
"path"
"path/filepath"
yaml "gopkg.in/yaml.v2"
)
const (
workspaceFilename = "WORKSPACE"
aspectpluginsFilename = ".aspectplugins"
)
// AspectPlugin represents a plugin entry in the plugins file.
type AspectPlugin struct {
Name string `yaml:"name"`
From string `yaml:"from"`
LogLevel string `yaml:"log_level"`
Properties map[string]interface{} `yaml:"properties"`
}
// Finder is the interface that wraps the simple Find method that performs the
// finding of the plugins file in the user system.
type Finder interface {
Find() (string, error)
}
type finder struct {
osGetwd func() (string, error)
osStat func(string) (fs.FileInfo, error)
}
// NewFinder instantiates a default internal implementation of the Finder
// interface.
func NewFinder() Finder {
return &finder{
osGetwd: os.Getwd,
osStat: os.Stat,
}
}
// Find finds the .aspectplugins file under a Bazel workspace. If the returned
// path is empty and no error was produced, the file doesn't exist.
func (f *finder) Find() (string, error) {
cwd, err := f.osGetwd()
if err != nil {
return "", fmt.Errorf("failed to find .aspectplugins: %w", err)
}
for {
workspacePath := path.Join(cwd, workspaceFilename)
if _, err := f.osStat(workspacePath); err != nil {
if !os.IsNotExist(err) {
return "", fmt.Errorf("failed to find .aspectplugins: %w", err)
}
cwd = filepath.Dir(cwd)
continue
}
aspectpluginsPath := path.Join(cwd, aspectpluginsFilename)
if _, err := f.osStat(aspectpluginsPath); err != nil {
if !os.IsNotExist(err) {
return "", fmt.Errorf("failed to find .aspectplugins: %w", err)
}
break
}
return aspectpluginsPath, nil
}
return "", nil
}
// Parser is the interface that wraps the Parse method that performs the parsing
// of a plugins file.
type Parser interface {
Parse(aspectpluginsPath string) ([]AspectPlugin, error)
}
type parser struct {
ioutilReadFile func(filename string) ([]byte, error)
yamlUnmarshalStrict func(in []byte, out interface{}) (err error)
}
// NewParser instantiates a default internal implementation of the Parser
// interface.
func NewParser() Parser {
return &parser{
ioutilReadFile: ioutil.ReadFile,
yamlUnmarshalStrict: yaml.UnmarshalStrict,
}
}
// Parse parses a plugins file.
func (p *parser) Parse(aspectpluginsPath string) ([]AspectPlugin, error) {
if aspectpluginsPath == "" {
return []AspectPlugin{}, nil
}
aspectpluginsData, err := p.ioutilReadFile(aspectpluginsPath)
if err != nil {
return nil, fmt.Errorf("failed to parse .aspectplugins: %w", err)
}
var aspectplugins []AspectPlugin
if err := p.yamlUnmarshalStrict(aspectpluginsData, &aspectplugins); err != nil {
return nil, fmt.Errorf("failed to parse .aspectplugins: %w", err)
}
return aspectplugins, nil
}