119 lines
2.3 KiB
Go
119 lines
2.3 KiB
Go
package get
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/go-git/go-git/v5"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Parse parses an AST
|
|
func Parse(contents []byte) (*AST, error) {
|
|
directives := make([]Directive, 0)
|
|
comments := make([]string, 0)
|
|
errs := make([]error, 0)
|
|
|
|
scanner := bufio.NewScanner(bytes.NewBuffer(contents))
|
|
for scanner.Scan() {
|
|
text := os.ExpandEnv(scanner.Text())
|
|
|
|
// Comments
|
|
if strings.HasPrefix(text, "#") {
|
|
comments = append(comments, strings.TrimLeft(text, "# "))
|
|
continue
|
|
}
|
|
|
|
// Directive
|
|
line := strings.Fields(strings.TrimSpace(text))
|
|
if len(line) <= 1 {
|
|
continue
|
|
}
|
|
typ, err := ParseType(line[0])
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
continue
|
|
}
|
|
args := line[1:]
|
|
|
|
switch typ {
|
|
case CMD:
|
|
if len(args) < 1 {
|
|
err = errors.New("CMD requires 1 or more arguments")
|
|
}
|
|
case COPY:
|
|
if len(args) != 2 {
|
|
err = fmt.Errorf("COPY requires 2 arguments but got %d", len(args))
|
|
}
|
|
case ECHO:
|
|
err = nil // Just explicitly set it
|
|
case FETCH:
|
|
if len(args) != 2 {
|
|
err = fmt.Errorf("FETCH requires 2 arguments but got %d", len(args))
|
|
}
|
|
case GET:
|
|
if len(args) != 1 {
|
|
err = fmt.Errorf("GET requires 1 argument but got %d", len(args))
|
|
}
|
|
}
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
continue
|
|
}
|
|
|
|
directives = append(directives, Directive{
|
|
Type: typ,
|
|
Args: args,
|
|
Comments: comments,
|
|
ParseError: err,
|
|
})
|
|
comments = make([]string, 0)
|
|
}
|
|
|
|
var err error
|
|
if len(errs) > 0 {
|
|
err = ErrInvalid{Errors: errs}
|
|
}
|
|
|
|
return &AST{
|
|
Directives: directives,
|
|
}, err
|
|
}
|
|
|
|
// ParseFile will parse an AST from a file
|
|
func ParseFile(filename string) (*AST, error) {
|
|
contents, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Parse(contents)
|
|
}
|
|
|
|
// ParseRepo will clone a repository and search for a Getfile to parse an AST
|
|
// It is the caller's responsibility to clean up the clone from <base>
|
|
func ParseRepo(base, url string) (*AST, error) {
|
|
_, err := git.PlainClone(base, false, &git.CloneOptions{
|
|
URL: url,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ParseFile(filepath.Join(base, "Getfile"))
|
|
}
|
|
|
|
type ErrInvalid struct {
|
|
Errors []error
|
|
}
|
|
|
|
func (e ErrInvalid) Error() string {
|
|
errs := make([]string, len(e.Errors))
|
|
for idx, err := range e.Errors {
|
|
errs[idx] = err.Error()
|
|
}
|
|
return strings.Join(errs, "\n")
|
|
}
|