create internal/buildfile reader+errors | tests for reader [only check syntax and extract without command-specific splitting]
This commit is contained in:
parent
54f8003135
commit
b355ac5bf5
|
@ -0,0 +1,14 @@
|
||||||
|
package buildfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LineError struct {
|
||||||
|
Line int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (le LineError) Error() string {
|
||||||
|
return fmt.Sprintf("line %d | %s", le.Line, le.Err.Error())
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package buildfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidSyntax = errors.New("invalid instruction format")
|
||||||
|
var ErrUnknownInstruction = errors.New("unknown instruction")
|
||||||
|
|
||||||
|
type instruction_type byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// INSTALL one or more package(s)
|
||||||
|
INSTALL instruction_type = iota
|
||||||
|
// UPDATE all installed packages candidates
|
||||||
|
UPDATE
|
||||||
|
// REMOVE one or more package(s)
|
||||||
|
REMOVE
|
||||||
|
// SERVICE management (enable, disable, start, stop, restart, reload)
|
||||||
|
SERVICE
|
||||||
|
// CONF edits configuration files
|
||||||
|
CONF
|
||||||
|
)
|
||||||
|
|
||||||
|
// instruction is self-explanatory
|
||||||
|
type instruction struct {
|
||||||
|
Type instruction_type
|
||||||
|
// Sub command instruction-type-specific
|
||||||
|
Sub []string
|
||||||
|
Args string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader is the buildfile reader
|
||||||
|
type Reader struct {
|
||||||
|
// linux distribution to run on
|
||||||
|
distribution string
|
||||||
|
Content []*instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader creates a new reader for the specified build file and linux distribution
|
||||||
|
func NewReader(distro string, buildfile io.Reader) (*Reader, error) {
|
||||||
|
|
||||||
|
r := &Reader{
|
||||||
|
distribution: distro,
|
||||||
|
Content: make([]*instruction, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// read each line
|
||||||
|
l, reader := 0, bufio.NewReader(buildfile)
|
||||||
|
for {
|
||||||
|
l++
|
||||||
|
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, LineError{l, err}
|
||||||
|
}
|
||||||
|
line = strings.Trim(line, " \t\n")
|
||||||
|
|
||||||
|
// check line base syntax 'cmd args...'
|
||||||
|
if len(line) < 5 || len(strings.Trim(line[0:3], " ")) < 3 || line[1] == ' ' || line[3] != ' ' {
|
||||||
|
return nil, LineError{l, ErrInvalidSyntax}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build instruction from line
|
||||||
|
inst := &instruction{
|
||||||
|
Type: INSTALL,
|
||||||
|
Sub: make([]string, 0),
|
||||||
|
Args: line[4:],
|
||||||
|
}
|
||||||
|
|
||||||
|
switch line[0:3] {
|
||||||
|
case "ins":
|
||||||
|
inst.Type = INSTALL
|
||||||
|
case "upd":
|
||||||
|
inst.Type = UPDATE
|
||||||
|
case "del":
|
||||||
|
inst.Type = REMOVE
|
||||||
|
case "ser":
|
||||||
|
inst.Type = SERVICE
|
||||||
|
case "cnf":
|
||||||
|
inst.Type = CONF
|
||||||
|
default:
|
||||||
|
return nil, LineError{l, ErrUnknownInstruction}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Content = append(r.Content, inst)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package buildfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInstructionSyntax(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Err error
|
||||||
|
}{
|
||||||
|
{"ins args\ncnf args\n", 0, nil},
|
||||||
|
{" ins args\ncnf args\n", 0, nil},
|
||||||
|
{"\tins args\ncnf args\n", 0, nil},
|
||||||
|
{" \t ins args\ncnf args\n", 0, nil},
|
||||||
|
{" \t ins args\ncnf args\n", 0, nil},
|
||||||
|
|
||||||
|
{"cmd args\ncmd args\n", 1, ErrUnknownInstruction},
|
||||||
|
{"ins args\ncmd args\n", 2, ErrUnknownInstruction},
|
||||||
|
{" cmd args\ncmd args\n", 1, ErrUnknownInstruction},
|
||||||
|
{"\tcmd args\ncmd args\n", 1, ErrUnknownInstruction},
|
||||||
|
{" \t cmd args\ncmd args\n", 1, ErrUnknownInstruction},
|
||||||
|
{" \t cmd args\ncmd args\n", 1, ErrUnknownInstruction},
|
||||||
|
|
||||||
|
{" md args\ncmd args\n", 1, ErrInvalidSyntax},
|
||||||
|
{"c d args\ncmd args\n", 1, ErrInvalidSyntax},
|
||||||
|
{"cm args\ncmd args\n", 1, ErrInvalidSyntax},
|
||||||
|
|
||||||
|
{"ins args\n md args\n", 2, ErrInvalidSyntax},
|
||||||
|
{"ins args\nc d args\n", 2, ErrInvalidSyntax},
|
||||||
|
{"ins args\ncm args\n", 2, ErrInvalidSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
|
||||||
|
// create reader
|
||||||
|
buffer := bytes.NewBufferString(test.File)
|
||||||
|
_, err := NewReader("ubuntu", buffer)
|
||||||
|
|
||||||
|
// no error expected
|
||||||
|
if test.Err == nil {
|
||||||
|
if err != nil {
|
||||||
|
lineerr := err.(LineError)
|
||||||
|
t.Errorf("[%d] expect no error, got <%s>", i, lineerr.Err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if err == nil {
|
||||||
|
t.Errorf("[%d] expect error <%s>, got none", i, test.Err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lineerr := err.(LineError)
|
||||||
|
|
||||||
|
// error expected
|
||||||
|
if lineerr.Err != test.Err {
|
||||||
|
t.Errorf("[%d] expect error <%s>, got <%s>", i, test.Err, lineerr.Err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if lineerr.Line != test.Line {
|
||||||
|
t.Errorf("[%d] expect error at line %d, got %d", i, test.Line, lineerr.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue